diff --git a/folly-clib.cabal b/folly-clib.cabal
--- a/folly-clib.cabal
+++ b/folly-clib.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.4
 name:               folly-clib
-version:            20250713.1537
+version:            20260203.1245
 synopsis:           The folly C++ library from Meta
 author:             Simon Marlow
 maintainer:         marlowsd@gmail.com
@@ -31,7 +31,7 @@
   location: https://github.com/facebook/folly.git
 
 common fb-cpp
-  cxx-options: -std=c++17
+  cxx-options: -std=c++20
   if !flag(clang)
      cxx-options: -fcoroutines
   if arch(x86_64)
@@ -64,6 +64,8 @@
         -- The contents of cxx-sources and install-includes get spliced
         -- in by running 'make setup-folly'
         cxx-sources:
+                        folly/folly/Benchmark.cpp 
+            folly/folly/BenchmarkUtil.cpp 
             folly/folly/CancellationToken.cpp 
             folly/folly/ClockGettimeWrappers.cpp 
             folly/folly/Conv.cpp 
@@ -75,39 +77,40 @@
             folly/folly/FileUtil.cpp 
             folly/folly/Fingerprint.cpp 
             folly/folly/FmtUtility.cpp 
-            folly/folly/FollyMemcpy.cpp 
-            folly/folly/FollyMemset.cpp 
             folly/folly/Format.cpp 
             folly/folly/GroupVarint.cpp 
+            folly/folly/FollyMemcpy.cpp 
+            folly/folly/FollyMemcpy.cpp 
+            folly/folly/memcpy.S 
+            folly/folly/FollyMemset.cpp 
+            folly/folly/FollyMemset.cpp 
+            folly/folly/MicroLock.cpp 
             folly/folly/IPAddress.cpp 
             folly/folly/IPAddressV4.cpp 
             folly/folly/IPAddressV6.cpp 
             folly/folly/MacAddress.cpp 
-            folly/folly/MicroLock.cpp 
+            folly/folly/SocketAddress.cpp 
             folly/folly/Random.cpp 
             folly/folly/ScopeGuard.cpp 
             folly/folly/SharedMutex.cpp 
             folly/folly/Singleton.cpp 
             folly/folly/SingletonThreadLocal.cpp 
-            folly/folly/SocketAddress.cpp 
             folly/folly/String.cpp 
             folly/folly/Subprocess.cpp 
             folly/folly/TimeoutQueue.cpp 
             folly/folly/Try.cpp 
             folly/folly/Unicode.cpp 
             folly/folly/Uri.cpp 
-            folly/folly/memcpy_select_aarch64.cpp 
-            folly/folly/memset_select_aarch64.cpp 
             folly/folly/algorithm/simd/Contains.cpp 
             folly/folly/channels/MaxConcurrentRateLimiter.cpp 
             folly/folly/cli/NestedCommandLineApp.cpp 
             folly/folly/cli/ProgramOptions.cpp 
             folly/folly/compression/Compression.cpp 
+            folly/folly/compression/Zlib.cpp 
+            folly/folly/compression/Zstd.cpp 
             folly/folly/compression/CompressionContextPoolSingletons.cpp 
             folly/folly/compression/QuotientMultiSet.cpp 
             folly/folly/compression/Select64.cpp 
-            folly/folly/compression/Zlib.cpp 
-            folly/folly/compression/Zstd.cpp 
             folly/folly/concurrency/CacheLocality.cpp 
             folly/folly/concurrency/DeadlockDetector.cpp 
             folly/folly/concurrency/ProcessLocalUniqueId.cpp 
@@ -115,16 +118,21 @@
             folly/folly/container/RegexMatchCache.cpp 
             folly/folly/container/detail/F14Table.cpp 
             folly/folly/coro/Baton.cpp 
+            folly/folly/coro/detail/Malloc.cpp 
             folly/folly/coro/Mutex.cpp 
             folly/folly/coro/SerialQueueRunner.cpp 
             folly/folly/coro/SharedMutex.cpp 
-            folly/folly/coro/detail/Malloc.cpp 
             folly/folly/crypto/Blake2xb.cpp 
             folly/folly/crypto/LtHash.cpp 
+            folly/folly/crypto/LtHash.cpp 
+            folly/folly/crypto/LtHash.cpp 
+            folly/folly/crypto/detail/MathOperation_Simple.cpp 
+            folly/folly/crypto/detail/MathOperation_SSE2.cpp 
             folly/folly/crypto/detail/MathOperation_AVX2.cpp 
             folly/folly/crypto/detail/MathOperation_SSE2.cpp 
-            folly/folly/crypto/detail/MathOperation_Simple.cpp 
+            folly/folly/crypto/detail/MathOperation_AVX2.cpp 
             folly/folly/debugging/exception_tracer/ExceptionCounterLib.cpp 
+            folly/folly/debugging/exception_tracer/ExceptionCounterLibStaticRegistration.cpp 
             folly/folly/debugging/exception_tracer/ExceptionStackTraceLib.cpp 
             folly/folly/debugging/exception_tracer/ExceptionTracer.cpp 
             folly/folly/debugging/exception_tracer/ExceptionTracerLib.cpp 
@@ -157,46 +165,45 @@
             folly/folly/detail/RangeSse42.cpp 
             folly/folly/detail/SimpleSimdStringUtils.cpp 
             folly/folly/detail/SocketFastOpen.cpp 
-            folly/folly/detail/SplitStringSimd.cpp 
             folly/folly/detail/Sse.cpp 
             folly/folly/detail/StaticSingletonManager.cpp 
             folly/folly/detail/ThreadLocalDetail.cpp 
+            folly/folly/detail/thread_local_globals.cpp 
             folly/folly/detail/TrapOnAvx512.cpp 
             folly/folly/detail/UniqueInstance.cpp 
             folly/folly/detail/base64_detail/Base64Api.cpp 
-            folly/folly/detail/base64_detail/Base64SWAR.cpp 
             folly/folly/detail/base64_detail/Base64_SSE4_2.cpp 
-            folly/folly/detail/thread_local_globals.cpp 
-            folly/folly/executors/CPUThreadPoolExecutor.cpp 
+            folly/folly/detail/base64_detail/Base64SWAR.cpp 
             folly/folly/executors/Codel.cpp 
+            folly/folly/executors/CPUThreadPoolExecutor.cpp 
             folly/folly/executors/EDFThreadPoolExecutor.cpp 
             folly/folly/executors/ExecutionObserver.cpp 
             folly/folly/executors/ExecutorWithPriority.cpp 
             folly/folly/executors/FunctionScheduler.cpp 
             folly/folly/executors/GlobalExecutor.cpp 
             folly/folly/executors/GlobalThreadPoolList.cpp 
+            folly/folly/executors/InlineExecutor.cpp 
             folly/folly/executors/IOThreadPoolDeadlockDetectorObserver.cpp 
             folly/folly/executors/IOThreadPoolExecutor.cpp 
-            folly/folly/executors/InlineExecutor.cpp 
             folly/folly/executors/ManualExecutor.cpp 
             folly/folly/executors/QueueObserver.cpp 
             folly/folly/executors/QueuedImmediateExecutor.cpp 
             folly/folly/executors/SoftRealTimeExecutor.cpp 
             folly/folly/executors/StrandExecutor.cpp 
+            folly/folly/executors/StripedEDFThreadPoolExecutor.cpp 
             folly/folly/executors/ThreadPoolExecutor.cpp 
             folly/folly/executors/ThreadedExecutor.cpp 
             folly/folly/executors/ThreadedRepeatingFunctionRunner.cpp 
             folly/folly/executors/TimedDrivableExecutor.cpp 
             folly/folly/executors/TimekeeperScheduledExecutor.cpp 
             folly/folly/executors/thread_factory/PriorityThreadFactory.cpp 
-            folly/folly/ext/buck2/test_ext.cpp 
             folly/folly/ext/test_ext.cpp 
             folly/folly/external/farmhash/farmhash.cpp 
             folly/folly/external/fast-crc32/avx512_crc32c_v8s3x4.cpp 
+            folly/folly/external/fast-crc32/sse_crc32c_v8s3x3.cpp 
             folly/folly/external/fast-crc32/neon_crc32c_v3s4x2e_v2.cpp 
-            folly/folly/external/fast-crc32/neon_eor3_crc32_v9s3x2e_s3.cpp 
             folly/folly/external/fast-crc32/neon_eor3_crc32c_v8s2x4_s3.cpp 
-            folly/folly/external/fast-crc32/sse_crc32c_v8s3x3.cpp 
+            folly/folly/external/fast-crc32/neon_eor3_crc32_v9s3x2e_s3.cpp 
             folly/folly/external/nvidia/detail/RangeSve2.cpp 
             folly/folly/external/nvidia/hash/Checksum.cpp 
             folly/folly/external/nvidia/hash/detail/Crc32cDetail.cpp 
@@ -213,23 +220,24 @@
             folly/folly/futures/Barrier.cpp 
             folly/folly/futures/Future.cpp 
             folly/folly/futures/HeapTimekeeper.cpp 
-            folly/folly/futures/ManualTimekeeper.cpp 
             folly/folly/futures/Promise.cpp 
-            folly/folly/futures/SharedPromise.cpp 
             folly/folly/futures/ThreadWheelTimekeeper.cpp 
+            folly/folly/futures/ManualTimekeeper.cpp 
+            folly/folly/futures/SharedPromise.cpp 
             folly/folly/futures/detail/Core.cpp 
             folly/folly/hash/Checksum.cpp 
             folly/folly/hash/SpookyHashV1.cpp 
             folly/folly/hash/SpookyHashV2.cpp 
+            folly/folly/hash/UniqueHashKey.cpp 
             folly/folly/hash/detail/ChecksumDetail.cpp 
             folly/folly/hash/detail/Crc32CombineDetail.cpp 
             folly/folly/hash/detail/Crc32cDetail.cpp 
             folly/folly/init/Init.cpp 
             folly/folly/init/Phase.cpp 
-            folly/folly/io/Cursor.cpp 
             folly/folly/io/FsUtil.cpp 
             folly/folly/io/GlobalShutdownSocketSet.cpp 
             folly/folly/io/HugePages.cpp 
+            folly/folly/io/Cursor.cpp 
             folly/folly/io/IOBuf.cpp 
             folly/folly/io/IOBufIovecBuilder.cpp 
             folly/folly/io/IOBufQueue.cpp 
@@ -237,98 +245,104 @@
             folly/folly/io/ShutdownSocketSet.cpp 
             folly/folly/io/SocketOptionMap.cpp 
             folly/folly/io/SocketOptionValue.cpp 
+            folly/folly/io/async/AsyncTimeout.cpp 
+            folly/folly/io/async/EventBase.cpp 
+            folly/folly/io/async/EventBaseBackendBase.cpp 
+            folly/folly/io/async/EventBaseLocal.cpp 
+            folly/folly/io/async/EventHandler.cpp 
+            folly/folly/io/async/HHWheelTimer.cpp 
+            folly/folly/io/async/TimeoutManager.cpp 
+            folly/folly/io/async/VirtualEventBase.cpp 
             folly/folly/io/async/AsyncBase.cpp 
             folly/folly/io/async/AsyncIO.cpp 
             folly/folly/io/async/AsyncIoUringSocket.cpp 
+            folly/folly/io/async/AsyncIoUringSocketFactory.cpp 
             folly/folly/io/async/AsyncPipe.cpp 
-            folly/folly/io/async/AsyncSSLSocket.cpp 
-            folly/folly/io/async/AsyncServerSocket.cpp 
             folly/folly/io/async/AsyncSignalHandler.cpp 
             folly/folly/io/async/AsyncSocket.cpp 
             folly/folly/io/async/AsyncSocketException.cpp 
             folly/folly/io/async/AsyncSocketTransport.cpp 
-            folly/folly/io/async/AsyncTimeout.cpp 
+            folly/folly/io/async/AsyncSSLSocket.cpp 
             folly/folly/io/async/AsyncUDPSocket.cpp 
             folly/folly/io/async/DelayedDestruction.cpp 
             folly/folly/io/async/EpollBackend.cpp 
-            folly/folly/io/async/EventBase.cpp 
-            folly/folly/io/async/EventBaseBackendBase.cpp 
-            folly/folly/io/async/EventBaseLocal.cpp 
             folly/folly/io/async/EventBaseManager.cpp 
             folly/folly/io/async/EventBasePoller.cpp 
             folly/folly/io/async/EventBaseThread.cpp 
-            folly/folly/io/async/EventHandler.cpp 
-            folly/folly/io/async/HHWheelTimer.cpp 
             folly/folly/io/async/IoUring.cpp 
             folly/folly/io/async/IoUringBackend.cpp 
+            folly/folly/io/async/IoUringConnect.cpp 
             folly/folly/io/async/IoUringEvent.cpp 
             folly/folly/io/async/IoUringEventBaseLocal.cpp 
             folly/folly/io/async/IoUringProvidedBufferRing.cpp 
+            folly/folly/io/async/IoUringRecv.cpp 
+            folly/folly/io/async/IoUringSend.cpp 
             folly/folly/io/async/IoUringZeroCopyBufferPool.cpp 
             folly/folly/io/async/MuxIOThreadPoolExecutor.cpp 
             folly/folly/io/async/PasswordInFile.cpp 
             folly/folly/io/async/Request.cpp 
-            folly/folly/io/async/SSLContext.cpp 
-            folly/folly/io/async/SSLOptions.cpp 
-            folly/folly/io/async/STTimerFDTimeoutManager.cpp 
             folly/folly/io/async/ScopedEventBaseThread.cpp 
+            folly/folly/io/async/AsyncServerSocket.cpp 
             folly/folly/io/async/SimpleAsyncIO.cpp 
+            folly/folly/io/async/SSLContext.cpp 
+            folly/folly/io/async/SSLOptions.cpp 
             folly/folly/io/async/TerminateCancellationToken.cpp 
-            folly/folly/io/async/TimeoutManager.cpp 
+            folly/folly/io/async/STTimerFDTimeoutManager.cpp 
             folly/folly/io/async/TimerFD.cpp 
             folly/folly/io/async/TimerFDTimeoutManager.cpp 
-            folly/folly/io/async/VirtualEventBase.cpp 
             folly/folly/io/async/fdsock/AsyncFdSocket.cpp 
             folly/folly/io/async/fdsock/SocketFds.cpp 
             folly/folly/io/async/ssl/OpenSSLUtils.cpp 
             folly/folly/io/async/ssl/SSLErrors.cpp 
             folly/folly/io/coro/ServerSocket.cpp 
             folly/folly/io/coro/Transport.cpp 
+            folly/folly/json/dynamic.cpp 
+            folly/folly/json/json.cpp 
             folly/folly/json/DynamicParser.cpp 
+            folly/folly/json/json_patch.cpp 
+            folly/folly/json/json_pointer.cpp 
             folly/folly/json/JSONSchema.cpp 
             folly/folly/json/JsonTestUtil.cpp 
             folly/folly/json/bser/Dump.cpp 
             folly/folly/json/bser/Load.cpp 
-            folly/folly/json/dynamic.cpp 
-            folly/folly/json/json.cpp 
-            folly/folly/json/json_patch.cpp 
-            folly/folly/json/json_pointer.cpp 
             folly/folly/lang/CString.cpp 
             folly/folly/lang/Exception.cpp 
             folly/folly/lang/SafeAssert.cpp 
             folly/folly/lang/ToAscii.cpp 
             folly/folly/lang/UncaughtExceptions.cpp 
+            folly/folly/logging/FileHandlerFactory.cpp 
+            folly/folly/logging/BridgeFromGoogleLogging.cpp 
+            folly/folly/logging/Init.cpp 
+            folly/folly/logging/LogConfigParser.cpp 
+            folly/folly/logging/InitWeak.cpp 
+            folly/folly/logging/LogHandlerConfig.cpp 
+            folly/folly/logging/LogLevel.cpp 
+            folly/folly/logging/LogName.cpp 
             folly/folly/logging/AsyncFileWriter.cpp 
             folly/folly/logging/AsyncLogWriter.cpp 
-            folly/folly/logging/BridgeFromGoogleLogging.cpp 
             folly/folly/logging/CustomLogFormatter.cpp 
-            folly/folly/logging/FileHandlerFactory.cpp 
             folly/folly/logging/FileWriterFactory.cpp 
             folly/folly/logging/GlogStyleFormatter.cpp 
             folly/folly/logging/ImmediateFileWriter.cpp 
-            folly/folly/logging/Init.cpp 
-            folly/folly/logging/InitWeak.cpp 
             folly/folly/logging/LogCategory.cpp 
             folly/folly/logging/LogCategoryConfig.cpp 
             folly/folly/logging/LogConfig.cpp 
-            folly/folly/logging/LogConfigParser.cpp 
-            folly/folly/logging/LogHandlerConfig.cpp 
-            folly/folly/logging/LogLevel.cpp 
             folly/folly/logging/LogMessage.cpp 
-            folly/folly/logging/LogName.cpp 
             folly/folly/logging/LogStream.cpp 
             folly/folly/logging/LogStreamProcessor.cpp 
             folly/folly/logging/Logger.cpp 
             folly/folly/logging/LoggerDB.cpp 
             folly/folly/logging/ObjectToString.cpp 
-            folly/folly/logging/RateLimiter.cpp 
             folly/folly/logging/StandardLogHandler.cpp 
             folly/folly/logging/StandardLogHandlerFactory.cpp 
             folly/folly/logging/StreamHandlerFactory.cpp 
             folly/folly/logging/xlog.cpp 
+            folly/folly/logging/RateLimiter.cpp 
+            folly/folly/memory/IoUringArena.cpp 
             folly/folly/memory/JemallocHugePageAllocator.cpp 
             folly/folly/memory/JemallocNodumpAllocator.cpp 
             folly/folly/memory/MallctlHelper.cpp 
+            folly/folly/memory/Malloc.cpp 
             folly/folly/memory/ReentrantAllocator.cpp 
             folly/folly/memory/SanitizeAddress.cpp 
             folly/folly/memory/SanitizeLeak.cpp 
@@ -348,6 +362,7 @@
             folly/folly/portability/GFlags.cpp 
             folly/folly/portability/Libgen.cpp 
             folly/folly/portability/Malloc.cpp 
+            folly/folly/portability/openat2.c 
             folly/folly/portability/OpenSSL.cpp 
             folly/folly/portability/PThread.cpp 
             folly/folly/portability/Sched.cpp 
@@ -364,7 +379,13 @@
             folly/folly/portability/SysUio.cpp 
             folly/folly/portability/Time.cpp 
             folly/folly/portability/Unistd.cpp 
+            folly/folly/result/epitaph.cpp 
+            folly/folly/result/errc_rich_error.cpp 
             folly/folly/result/result.cpp 
+            folly/folly/result/rich_error_base.cpp 
+            folly/folly/result/rich_exception_ptr.cpp 
+            folly/folly/result/rich_error_code.cpp 
+            folly/folly/result/detail/rich_exception_ptr_storage.cpp 
             folly/folly/settings/CommandLineParser.cpp 
             folly/folly/settings/Immutables.cpp 
             folly/folly/settings/Settings.cpp 
@@ -388,8 +409,7 @@
             folly/folly/synchronization/ParkingLot.cpp 
             folly/folly/synchronization/Rcu.cpp 
             folly/folly/synchronization/SanitizeThread.cpp 
-            folly/folly/synchronization/WaitOptions.cpp 
-            folly/folly/synchronization/detail/Hardware.cpp 
+            folly/folly/synchronization/StripedThrottledLifoSem.cpp 
             folly/folly/synchronization/detail/Sleeper.cpp 
             folly/folly/system/AtFork.cpp 
             folly/folly/system/EnvUtil.cpp 
@@ -401,9 +421,6 @@
             folly/folly/system/ThreadName.cpp 
             folly/folly/testing/TestUtil.cpp 
             folly/folly/tracing/AsyncStack.cpp 
-            folly/folly/io/async/test/ScopedBoundPort.cpp 
-            folly/folly/io/async/test/SocketPair.cpp 
-            folly/folly/io/async/test/TimeUtil.cpp
 
         install-includes:
             folly/AtomicHashArray-inl.h 
@@ -621,6 +638,7 @@
             folly/container/Access.h 
             folly/container/Array.h 
             folly/container/BitIterator.h 
+            folly/container/CollectionUtil.h 
             folly/container/Enumerate.h 
             folly/container/EvictingCacheMap.h 
             folly/container/F14Map-fwd.h 
@@ -655,6 +673,7 @@
             folly/container/detail/Util.h 
             folly/container/detail/tape_detail.h 
             folly/container/heap_vector_types.h 
+            folly/container/irange.h 
             folly/container/range_traits.h 
             folly/container/small_vector.h 
             folly/container/sorted_vector_types.h 
@@ -669,8 +688,7 @@
             folly/coro/AsyncStack.h 
             folly/coro/AutoCleanup-fwd.h 
             folly/coro/AutoCleanup.h 
-            folly/coro/AwaitImmediately.h 
-            folly/coro/AwaitResult.h 
+            folly/coro/BasePromise.h 
             folly/coro/Baton.h 
             folly/coro/BlockingWait.h 
             folly/coro/BoundedQueue.h 
@@ -682,6 +700,7 @@
             folly/coro/Coroutine.h 
             folly/coro/CurrentExecutor.h 
             folly/coro/DetachOnCancel.h 
+            folly/coro/Error.h 
             folly/coro/Filter-inl.h 
             folly/coro/Filter.h 
             folly/coro/FutureUtil.h 
@@ -692,9 +711,8 @@
             folly/coro/Merge-inl.h 
             folly/coro/Merge.h 
             folly/coro/Mutex.h 
-            folly/coro/Noexcept.h 
+            folly/coro/Nothrow.h 
             folly/coro/Promise.h 
-            folly/coro/Ready.h 
             folly/coro/Result.h 
             folly/coro/Retry.h 
             folly/coro/RustAdaptors.h 
@@ -716,6 +734,8 @@
             folly/coro/Transform-inl.h 
             folly/coro/Transform.h 
             folly/coro/UnboundedQueue.h 
+            folly/coro/ValueOrError.h 
+            folly/coro/ValueOrFatal.h 
             folly/coro/ViaIfAsync.h 
             folly/coro/WithAsyncStack.h 
             folly/coro/WithCancellation.h 
@@ -730,12 +750,12 @@
             folly/coro/detail/Traits.h 
             folly/coro/safe/AsyncClosure-fwd.h 
             folly/coro/safe/AsyncClosure.h 
+            folly/coro/safe/BindCaptures.h 
             folly/coro/safe/Captures.h 
             folly/coro/safe/NowTask.h 
-            folly/coro/safe/SafeAlias.h 
             folly/coro/safe/SafeTask.h 
             folly/coro/safe/detail/AsyncClosure.h 
-            folly/coro/safe/detail/AsyncClosureBindings.h 
+            folly/coro/safe/detail/BindAsyncClosure.h 
             folly/coro/safe/detail/DefineMovableDeepConstLrefCopyable.h 
             folly/crypto/Blake2xb.h 
             folly/crypto/LtHash-inl.h 
@@ -839,6 +859,7 @@
             folly/executors/SerializedExecutor.h 
             folly/executors/SoftRealTimeExecutor.h 
             folly/executors/StrandExecutor.h 
+            folly/executors/StripedEDFThreadPoolExecutor.h 
             folly/executors/ThreadPoolExecutor.h 
             folly/executors/ThreadedExecutor.h 
             folly/executors/ThreadedRepeatingFunctionRunner.h 
@@ -849,6 +870,7 @@
             folly/executors/task_queue/LifoSemMPMCQueue.h 
             folly/executors/task_queue/PriorityLifoSemMPMCQueue.h 
             folly/executors/task_queue/PriorityUnboundedBlockingQueue.h 
+            folly/executors/task_queue/StripedPriorityUnboundedBlockingQueue.h 
             folly/executors/task_queue/UnboundedBlockingQueue.h 
             folly/executors/thread_factory/InitThreadFactory.h 
             folly/executors/thread_factory/NamedThreadFactory.h 
@@ -857,7 +879,6 @@
             folly/experimental/EventCount.h 
             folly/experimental/FlatCombiningPriorityQueue.h 
             folly/experimental/FunctionScheduler.h 
-            folly/experimental/TestUtil.h 
             folly/experimental/ThreadedRepeatingFunctionRunner.h 
             folly/experimental/channels/Channel-fwd.h 
             folly/experimental/channels/Channel-inl.h 
@@ -1112,11 +1133,15 @@
             folly/gen/String.h 
             folly/hash/Checksum.h 
             folly/hash/FarmHash.h 
+            folly/hash/FnvHash.h 
             folly/hash/Hash.h 
+            folly/hash/HsiehHash.h 
             folly/hash/MurmurHash.h 
             folly/hash/SpookyHashV1.h 
             folly/hash/SpookyHashV2.h 
+            folly/hash/UniqueHashKey.h 
             folly/hash/detail/ChecksumDetail.h 
+            folly/hash/detail/RandomSeed.h 
             folly/hash/rapidhash.h 
             folly/hash/traits.h 
             folly/init/Init.h 
@@ -1176,9 +1201,12 @@
             folly/io/async/IoUring.h 
             folly/io/async/IoUringBackend.h 
             folly/io/async/IoUringBase.h 
+            folly/io/async/IoUringConnect.h 
             folly/io/async/IoUringEvent.h 
             folly/io/async/IoUringEventBaseLocal.h 
             folly/io/async/IoUringProvidedBufferRing.h 
+            folly/io/async/IoUringRecv.h 
+            folly/io/async/IoUringSend.h 
             folly/io/async/IoUringZeroCopyBufferPool.h 
             folly/io/async/Liburing.h 
             folly/io/async/MuxIOThreadPoolExecutor.h 
@@ -1227,7 +1255,6 @@
             folly/lang/Aligned.h 
             folly/lang/Assume.h 
             folly/lang/Badge.h 
-            folly/lang/Bindings.h 
             folly/lang/Bits.h 
             folly/lang/BitsClass.h 
             folly/lang/Builtin.h 
@@ -1241,21 +1268,30 @@
             folly/lang/Hint-inl.h 
             folly/lang/Hint.h 
             folly/lang/Keep.h 
+            folly/lang/MustUseImmediately.h 
             folly/lang/New.h 
             folly/lang/Ordering.h 
             folly/lang/Pretty.h 
             folly/lang/PropagateConst.h 
             folly/lang/RValueReferenceWrapper.h 
             folly/lang/SafeAlias-fwd.h 
+            folly/lang/SafeAlias.h 
             folly/lang/SafeAssert.h 
+            folly/lang/SafeClosure.h 
             folly/lang/StaticConst.h 
+            folly/lang/Strong.h 
             folly/lang/Switch.h 
             folly/lang/Thunk.h 
             folly/lang/ToAscii.h 
             folly/lang/TypeInfo.h 
             folly/lang/UncaughtExceptions.h 
             folly/lang/VectorTraits.h 
-            folly/lang/named/Bindings.h 
+            folly/lang/bind/AsArgument.h 
+            folly/lang/bind/Bind.h 
+            folly/lang/bind/Named.h 
+            folly/lang/bind/NamedToStorage.h 
+            folly/lang/bind/ToStorage.h 
+            folly/lang/cstring_view.h 
             folly/logging/AsyncFileWriter.h 
             folly/logging/AsyncLogWriter.h 
             folly/logging/AutoTimer.h 
@@ -1290,6 +1326,7 @@
             folly/logging/xlog.h 
             folly/memory/Arena-inl.h 
             folly/memory/Arena.h 
+            folly/memory/IoUringArena.h 
             folly/memory/JemallocHugePageAllocator.h 
             folly/memory/JemallocNodumpAllocator.h 
             folly/memory/MallctlHelper.h 
@@ -1371,18 +1408,46 @@
             folly/portability/Windows.h 
             folly/portability/openat2.h 
             folly/python/AsyncioExecutor.h 
+            folly/python/GILAwareManualExecutor.h 
+            folly/python/ProactorExecutor.h 
             folly/python/Weak.h 
             folly/python/async_generator.h 
             folly/python/coro.h 
             folly/python/error.h 
             folly/python/executor.h 
+            folly/python/fibers.h 
             folly/python/futures.h 
             folly/python/import.h 
             folly/python/iobuf.h 
+            folly/python/iobuf_ext.h 
+            folly/python/request_context.h 
+            folly/random/hash.h 
+            folly/random/seed_seq.h 
             folly/random/xoshiro256pp.h 
+            folly/result/coded_rich_error.h 
+            folly/result/coro.h 
+            folly/result/detail/immortal_exception_storage.h 
+            folly/result/detail/ptr_immortal_exception_storage.h 
+            folly/result/detail/result_or_unwind.h 
+            folly/result/detail/result_promise.h 
+            folly/result/detail/rich_error_common.h 
+            folly/result/detail/rich_exception_ptr_storage.h 
+            folly/result/epitaph.h 
+            folly/result/errc_rich_error.h 
             folly/result/gtest_helpers.h 
+            folly/result/immortal_rich_error.h 
+            folly/result/nestable_coded_rich_error.h 
+            folly/result/or_unwind_epitaph.h 
             folly/result/result.h 
+            folly/result/rich_error.h 
+            folly/result/rich_error_base.h 
+            folly/result/rich_error_code.h 
+            folly/result/rich_error_fwd.h 
+            folly/result/rich_exception_ptr.h 
+            folly/result/rich_msg.h 
             folly/result/try.h 
+            folly/result/value_only_result.h 
+            folly/result/value_only_result_coro.h 
             folly/settings/CommandLineParser.h 
             folly/settings/Immutables.h 
             folly/settings/Observer.h 
@@ -1458,10 +1523,10 @@
             folly/synchronization/SanitizeThread.h 
             folly/synchronization/SaturatingSemaphore.h 
             folly/synchronization/SmallLocks.h 
+            folly/synchronization/StripedThrottledLifoSem.h 
             folly/synchronization/ThrottledLifoSem.h 
             folly/synchronization/WaitOptions.h 
             folly/synchronization/detail/AtomicUtils.h 
-            folly/synchronization/detail/Hardware.h 
             folly/synchronization/detail/HazptrUtils.h 
             folly/synchronization/detail/InlineFunctionRef.h 
             folly/synchronization/detail/Sleeper.h 
@@ -1481,6 +1546,7 @@
             folly/system/Shell.h 
             folly/system/ThreadId.h 
             folly/system/ThreadName.h 
+            folly/system/arch/x86.h 
             folly/testing/TestUtil.h 
             folly/tracing/AsyncStack-inl.h 
             folly/tracing/AsyncStack.h 
diff --git a/folly/_build/folly/folly-config.h b/folly/_build/folly/folly-config.h
--- a/folly/_build/folly/folly-config.h
+++ b/folly/_build/folly/folly-config.h
@@ -56,20 +56,20 @@
 #define FOLLY_HAVE_WEAK_SYMBOLS 1
 #define FOLLY_HAVE_LINUX_VDSO 1
 #define FOLLY_HAVE_MALLOC_USABLE_SIZE 1
-/* #undef FOLLY_HAVE_INT128_T */
+#define FOLLY_HAVE_INT128_T 1
 #define FOLLY_HAVE_WCHAR_SUPPORT 1
 #define FOLLY_HAVE_EXTRANDOM_SFMT19937 1
 #define HAVE_VSNPRINTF_ERRORS 1
 
 #define FOLLY_HAVE_LIBUNWIND 1
-/* #undef FOLLY_HAVE_DWARF */
+#define FOLLY_HAVE_DWARF 0
 #define FOLLY_HAVE_ELF 1
 #define FOLLY_HAVE_SWAPCONTEXT 1
 #define FOLLY_HAVE_BACKTRACE 1
 #define FOLLY_USE_SYMBOLIZER 1
 #define FOLLY_DEMANGLE_MAX_SYMBOL_SIZE 1024
 
-#define FOLLY_HAVE_SHADOW_LOCAL_WARNINGS 1
+/* #undef FOLLY_HAVE_SHADOW_LOCAL_WARNINGS */
 
 #define FOLLY_HAVE_LIBLZ4 1
 #define FOLLY_HAVE_LIBLZMA 1
diff --git a/folly/folly/AtomicHashArray.h b/folly/folly/AtomicHashArray.h
--- a/folly/folly/AtomicHashArray.h
+++ b/folly/folly/AtomicHashArray.h
@@ -19,7 +19,7 @@
  *  core lock-free functionality, but is limited by the fact that it cannot
  *  grow past its initialization size and is a little more awkward (no public
  *  constructor, for example).  If you're confident that you won't run out of
- *  space, don't mind the awkardness, and really need bare-metal performance,
+ *  space, don't mind the awkwardness, and really need bare-metal performance,
  *  feel free to use AHA directly.
  *
  *  Check out AtomicHashMap.h for more thorough documentation on perf and
@@ -104,18 +104,18 @@
       "keys, or a different container class.");
 
  public:
-  typedef KeyT key_type;
-  typedef ValueT mapped_type;
-  typedef HashFcn hasher;
-  typedef EqualFcn key_equal;
-  typedef KeyConvertFcn key_convert;
-  typedef std::pair<const KeyT, ValueT> value_type;
-  typedef std::size_t size_type;
-  typedef std::ptrdiff_t difference_type;
-  typedef value_type& reference;
-  typedef const value_type& const_reference;
-  typedef value_type* pointer;
-  typedef const value_type* const_pointer;
+  using key_type = KeyT;
+  using mapped_type = ValueT;
+  using hasher = HashFcn;
+  using key_equal = EqualFcn;
+  using key_convert = KeyConvertFcn;
+  using value_type = std::pair<const KeyT, ValueT>;
+  using size_type = std::size_t;
+  using difference_type = std::ptrdiff_t;
+  using reference = value_type&;
+  using const_reference = const value_type&;
+  using pointer = value_type*;
+  using const_pointer = const value_type*;
 
   const size_t capacity_;
   const size_t maxEntries_;
@@ -126,12 +126,12 @@
   template <class ContT, class IterVal>
   struct aha_iterator;
 
-  typedef aha_iterator<const AtomicHashArray, const value_type> const_iterator;
-  typedef aha_iterator<AtomicHashArray, value_type> iterator;
+  using const_iterator = aha_iterator<const AtomicHashArray, const value_type>;
+  using iterator = aha_iterator<AtomicHashArray, value_type>;
 
   // You really shouldn't need this if you use the SmartPtr provided by create,
   // but if you really want to do something crazy like stick the released
-  // pointer into a DescriminatedPtr or something, you'll need this to clean up
+  // pointer into a DiscriminatedPtr or something, you'll need this to clean up
   // after yourself.
   static void destroy(AtomicHashArray*);
 
@@ -143,7 +143,7 @@
   };
 
  public:
-  typedef std::unique_ptr<AtomicHashArray, Deleter> SmartPtr;
+  using SmartPtr = std::unique_ptr<AtomicHashArray, Deleter>;
 
   /*
    * create --
diff --git a/folly/folly/AtomicIntrusiveLinkedList.h b/folly/folly/AtomicIntrusiveLinkedList.h
--- a/folly/folly/AtomicIntrusiveLinkedList.h
+++ b/folly/folly/AtomicIntrusiveLinkedList.h
@@ -40,7 +40,7 @@
   T* next{nullptr};
 };
 
-template <class T, AtomicIntrusiveLinkedListHook<T> T::*HookMember>
+template <class T, AtomicIntrusiveLinkedListHook<T> T::* HookMember>
 class AtomicIntrusiveLinkedList {
  public:
   AtomicIntrusiveLinkedList() {}
diff --git a/folly/folly/AtomicUnorderedMap.h b/folly/folly/AtomicUnorderedMap.h
--- a/folly/folly/AtomicUnorderedMap.h
+++ b/folly/folly/AtomicUnorderedMap.h
@@ -122,13 +122,12 @@
 ///
 /// MEMORY ALLOCATION
 ///
-/// Underlying memory is allocated as a big anonymous mmap chunk, which
-/// might be cheaper than calloc() and is certainly not more expensive
-/// for large maps.  If the SkipKeyValueDeletion template param is true
-/// then deletion of the map consists of unmapping the backing memory,
-/// which is much faster than destructing all of the keys and values.
-/// Feel free to override if std::is_trivial_destructor isn't recognizing
-/// the triviality of your destructors.
+/// Underlying memory is allocated as a big anonymous chunk. If the
+/// SkipKeyValueDeletion template param is true then deletion of the map
+/// consists of deallocating the backing memory, which is much faster than
+/// destructing all of the keys and values.  Feel free to override if
+/// std::is_trivial_destructor isn't recognizing the triviality of your
+/// destructors.
 template <
     typename Key,
     typename Value,
@@ -139,7 +138,7 @@
          std::is_trivially_destructible<Value>::value),
     template <typename> class Atom = std::atomic,
     typename IndexType = uint32_t,
-    typename Allocator = folly::detail::MMapAlloc>
+    typename Allocator = folly::detail::MallocAlloc>
 
 struct AtomicUnorderedInsertMap {
   using key_type = Key;
@@ -222,8 +221,8 @@
 
     numSlots_ = capacity;
     slotMask_ = folly::nextPowTwo(capacity * 4) - 1;
-    mmapRequested_ = sizeof(Slot) * capacity;
-    slots_ = reinterpret_cast<Slot*>(allocator_.allocate(mmapRequested_));
+    allocRequested_ = sizeof(Slot) * capacity;
+    slots_ = reinterpret_cast<Slot*>(allocator_.allocate(allocRequested_));
     zeroFillSlots();
     // mark the zero-th slot as in-use but not valid, since that happens
     // to be our nil value
@@ -236,7 +235,7 @@
         slots_[i].~Slot();
       }
     }
-    allocator_.deallocate(reinterpret_cast<char*>(slots_), mmapRequested_);
+    allocator_.deallocate(reinterpret_cast<char*>(slots_), allocRequested_);
   }
 
   /// Searches for the key, returning (iter,false) if it is found.
@@ -404,7 +403,7 @@
   // (by getting a zero-filled mmap chunk) and optionally destruction of
   // the slots
 
-  size_t mmapRequested_;
+  size_t allocRequested_;
   size_t numSlots_;
 
   /// tricky, see keyToSlotIdx
@@ -469,7 +468,7 @@
   void zeroFillSlots() {
     using folly::detail::GivesZeroFilledMemory;
     if (!GivesZeroFilledMemory<Allocator>::value) {
-      memset(static_cast<void*>(slots_), 0, mmapRequested_);
+      memset(static_cast<void*>(slots_), 0, allocRequested_);
     }
   }
 };
@@ -487,7 +486,7 @@
         (std::is_trivially_destructible<Key>::value &&
          std::is_trivially_destructible<Value>::value),
     template <typename> class Atom = std::atomic,
-    typename Allocator = folly::detail::MMapAlloc>
+    typename Allocator = folly::detail::MallocAlloc>
 using AtomicUnorderedInsertMap64 = AtomicUnorderedInsertMap<
     Key,
     Value,
diff --git a/folly/folly/Benchmark.cpp b/folly/folly/Benchmark.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/Benchmark.cpp
@@ -0,0 +1,1036 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/Benchmark.h>
+
+#include <algorithm>
+#include <cmath>
+#include <cstring>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <memory>
+#include <numeric>
+#include <utility>
+#include <vector>
+
+#include <folly/FileUtil.h>
+#include <folly/MapUtil.h>
+#include <folly/Overload.h>
+#include <folly/String.h>
+#include <folly/detail/PerfScoped.h>
+#include <folly/json/json.h>
+
+// This needs to be at the end because some versions end up including
+// Windows.h without defining NOMINMAX, which breaks uses
+// of `std::numeric_limits<T>::max()`. We explicitly define NOMINMAX here
+// explicitly instead.
+#define NOMINMAX 1
+#include <boost/regex.hpp>
+
+using namespace std;
+
+FOLLY_GFLAGS_DEFINE_bool(benchmark, false, "Run benchmarks.");
+
+FOLLY_GFLAGS_DEFINE_bool(json, false, "Output in JSON format.");
+
+FOLLY_GFLAGS_DEFINE_bool(
+    bm_estimate_time,
+    false,
+    "Estimate running time by returning the geometric mean of latency values between p25 and p75.");
+
+#if FOLLY_PERF_IS_SUPPORTED
+FOLLY_GFLAGS_DEFINE_string(
+    bm_perf_args,
+    "",
+    "Run selected benchmarks while attaching `perf` profiling tool."
+    "Advantage over attaching perf externally is that this skips "
+    "initialization. The first iteration of the benchmark is also "
+    "skipped to allow for all statics to be set up. This requires perf "
+    " to be available on the system. Example: --bm_perf_args=\"record -g\"");
+#endif
+
+FOLLY_GFLAGS_DEFINE_bool(
+    bm_profile, false, "Run benchmarks with constant number of iterations");
+
+FOLLY_GFLAGS_DEFINE_int64(
+    bm_profile_iters, 1000, "Number of iterations for profiling");
+
+FOLLY_GFLAGS_DEFINE_string(
+    bm_relative_to,
+    "",
+    "Print benchmark results relative to an earlier dump (via --bm_json_verbose)");
+
+FOLLY_GFLAGS_DEFINE_bool(
+    bm_warm_up_iteration,
+    false,
+    "Run one iteration of the benchmarks before measuring. Always true if `bm_perf_args` is passed");
+
+FOLLY_GFLAGS_DEFINE_string(
+    bm_json_verbose,
+    "",
+    "File to write verbose JSON format (for BenchmarkCompare / --bm_relative_to). "
+    "NOTE: this file is written regardless of options --json and --bm_relative_to.");
+
+FOLLY_GFLAGS_DEFINE_string(
+    bm_regex, "", "Only benchmarks whose names match this regex will be run.");
+
+FOLLY_GFLAGS_DEFINE_string(
+    bm_file_regex,
+    "",
+    "Only benchmarks whose filenames match this regex will be run.");
+
+FOLLY_GFLAGS_DEFINE_int64(
+    bm_min_usec,
+    100,
+    "Minimum # of microseconds we'll accept for each benchmark.");
+
+FOLLY_GFLAGS_DEFINE_int32(
+    bm_min_iters, 1, "Minimum # of iterations we'll try for each benchmark.");
+
+FOLLY_GFLAGS_DEFINE_int64(
+    bm_max_iters,
+    1 << 30,
+    "Maximum # of iterations we'll try for each benchmark.");
+
+FOLLY_GFLAGS_DEFINE_int32(
+    bm_max_secs, 1, "Maximum # of seconds we'll spend on each benchmark.");
+
+FOLLY_GFLAGS_DEFINE_uint32(
+    bm_result_width_chars, 76, "Width of results table in characters");
+
+FOLLY_GFLAGS_DEFINE_uint32(
+    bm_max_trials,
+    1000,
+    "Maximum number of trials (iterations) executed for each benchmark.");
+
+FOLLY_GFLAGS_DEFINE_bool(
+    bm_list,
+    false,
+    "Print out list of all benchmark test names without running them.");
+
+namespace folly {
+namespace detail {
+
+BenchmarkingState<std::chrono::high_resolution_clock>& globalBenchmarkState() {
+  static detail::BenchmarkingState<std::chrono::high_resolution_clock> state;
+  return state;
+}
+
+} // namespace detail
+
+using BenchmarkFun = std::function<detail::TimeIterData(unsigned int)>;
+
+#define FB_FOLLY_GLOBAL_BENCHMARK_BASELINE fbFollyGlobalBenchmarkBaseline
+#define FB_FOLLY_GLOBAL_BENCHMARK_SUSPENDER_BASELINE \
+  fbFollyGlobalBenchmarkSuspenderBaseline
+#define FB_STRINGIZE_X2(x) FOLLY_PP_STRINGIZE(x)
+
+constexpr const char kGlobalBenchmarkBaseline[] =
+    FB_STRINGIZE_X2(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE);
+
+constexpr const char kGlobalBenchmarkSuspenderBaseline[] =
+    FB_STRINGIZE_X2(FB_FOLLY_GLOBAL_BENCHMARK_SUSPENDER_BASELINE);
+
+// Add the global baseline
+BENCHMARK(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE) {
+#ifdef _MSC_VER
+  _ReadWriteBarrier();
+#else
+  asm volatile("");
+#endif
+}
+
+// Add the suspender overhead baseline
+BENCHMARK(FB_FOLLY_GLOBAL_BENCHMARK_SUSPENDER_BASELINE) {
+  BENCHMARK_SUSPEND {}
+}
+
+#undef FB_STRINGIZE_X2
+#undef FB_FOLLY_GLOBAL_BENCHMARK_BASELINE
+
+static std::pair<double, UserCounters> runBenchmarkGetNSPerIteration(
+    const BenchmarkFun& fun, const double globalBaseline) {
+  using std::chrono::duration_cast;
+  using std::chrono::high_resolution_clock;
+  using std::chrono::microseconds;
+  using std::chrono::nanoseconds;
+  using std::chrono::seconds;
+
+  // They key here is accuracy; too low numbers means the accuracy was
+  // coarse. We up the ante until we get to at least minNanoseconds
+  // timings.
+  static_assert(
+      std::is_same<high_resolution_clock::duration, nanoseconds>::value,
+      "High resolution clock must be nanosecond resolution.");
+  // We choose a minimum minimum (sic) of 100,000 nanoseconds, but if
+  // the clock resolution is worse than that, it will be larger. In
+  // essence we're aiming at making the quantization noise 0.01%.
+  static const auto minNanoseconds = std::max<nanoseconds>(
+      nanoseconds(100000), microseconds(FLAGS_bm_min_usec));
+
+  // We establish a total time budget as we don't want a measurement
+  // to take too long. This will curtail the number of actual trials.
+  const auto timeBudget = seconds(FLAGS_bm_max_secs);
+  auto global = high_resolution_clock::now();
+
+  std::vector<std::pair<double, UserCounters>> trialResults(
+      FLAGS_bm_max_trials);
+  size_t actualTrials = 0;
+
+  // We do measurements in several trials (epochs) and take the minimum, to
+  // account for jitter.
+  for (; actualTrials < FLAGS_bm_max_trials; ++actualTrials) {
+    const auto maxIters = uint32_t(FLAGS_bm_max_iters);
+    for (auto n = uint32_t(FLAGS_bm_min_iters); n < maxIters; n *= 2) {
+      detail::TimeIterData timeIterData = fun(static_cast<unsigned int>(n));
+      if (timeIterData.duration < minNanoseconds) {
+        continue;
+      }
+      // We got an accurate enough timing, done. But only save if
+      // smaller than the current result.
+      auto nsecs = duration_cast<nanoseconds>(timeIterData.duration);
+      trialResults[actualTrials] = std::make_pair(
+          max(0.0, double(nsecs.count()) / timeIterData.niter - globalBaseline),
+          std::move(timeIterData.userCounters));
+      // Done with the current trial, we got a meaningful timing.
+      break;
+    }
+    auto now = high_resolution_clock::now();
+    if (now - global >= timeBudget) {
+      // No more time budget available.
+      ++actualTrials;
+      break;
+    }
+  }
+
+  // Current state of the art: get the minimum. After some
+  // experimentation, it seems taking the minimum is the best.
+  auto iter = min_element(
+      trialResults.begin(),
+      trialResults.begin() + actualTrials,
+      [](const auto& a, const auto& b) { return a.first < b.first; });
+
+  // If the benchmark was basically drowned in baseline noise, it's
+  // possible it became negative.
+  return std::make_pair(max(0.0, iter->first), iter->second);
+}
+
+static std::pair<double, UserCounters> runBenchmarkGetNSPerIterationEstimate(
+    const BenchmarkFun& fun, const double globalBaseline) {
+  using std::chrono::duration_cast;
+  using std::chrono::high_resolution_clock;
+  using std::chrono::microseconds;
+  using std::chrono::nanoseconds;
+  using std::chrono::seconds;
+  using TrialResultType = std::pair<double, UserCounters>;
+
+  // They key here is accuracy; too low numbers means the accuracy was
+  // coarse. We up the ante until we get to at least minNanoseconds
+  // timings.
+  static_assert(
+      std::is_same<high_resolution_clock::duration, nanoseconds>::value,
+      "High resolution clock must be nanosecond resolution.");
+
+  // Estimate single iteration running time for 1 sec
+  double estPerIter = 0.0; // Estimated nanosec per iteration
+  auto estStart = high_resolution_clock::now();
+  const auto estBudget = seconds(1);
+  for (auto n = 1; n < 1000; n *= 2) {
+    detail::TimeIterData timeIterData = fun(static_cast<unsigned int>(n));
+    auto now = high_resolution_clock::now();
+    auto nsecs = duration_cast<nanoseconds>(timeIterData.duration);
+    estPerIter = double(nsecs.count() - globalBaseline) / n;
+    if (now - estStart > estBudget) {
+      break;
+    }
+  }
+  // Can't estimate running time, so make it a baseline
+  if (estPerIter <= 0.0) {
+    estPerIter = globalBaseline;
+  }
+
+  // We do measurements in several trials (epochs) to account for jitter.
+  size_t actualTrials = 0;
+  const unsigned int estimateCount = to_integral(max(1.0, 5e+7 / estPerIter));
+  std::vector<TrialResultType> trialResults(FLAGS_bm_max_trials);
+  const auto maxRunTime = seconds(max(5, FLAGS_bm_max_secs));
+  auto globalStart = high_resolution_clock::now();
+
+  // Run benchmark up to trial times with at least 0.5 sec each
+  // Or until we run out of allowed time (max(5, FLAGS_bm_max_secs))
+  for (size_t tryId = 0; tryId < FLAGS_bm_max_trials; tryId++) {
+    detail::TimeIterData timeIterData = fun(estimateCount);
+    auto nsecs = duration_cast<nanoseconds>(timeIterData.duration);
+
+    if (nsecs.count() > globalBaseline) {
+      auto nsecIter =
+          double(nsecs.count() - globalBaseline) / timeIterData.niter;
+      trialResults[actualTrials++] =
+          std::make_pair(nsecIter, std::move(timeIterData.userCounters));
+    }
+    // Check if we are out of time quota
+    auto now = high_resolution_clock::now();
+    if (now - globalStart > maxRunTime) {
+      break;
+    }
+  }
+
+  // Sort results by running time
+  std::sort(
+      trialResults.begin(),
+      trialResults.begin() + actualTrials,
+      [](const TrialResultType& a, const TrialResultType& b) {
+        return a.first < b.first;
+      });
+
+  const auto getPercentile = [](size_t count, double p) -> size_t {
+    return static_cast<size_t>(count * p);
+  };
+
+  const size_t trialP25 = getPercentile(actualTrials, 0.25);
+  const size_t trialP75 = getPercentile(actualTrials, 0.75);
+  if (trialP75 - trialP25 == 0) {
+    // Use first trial results if p75 == p25.
+    return std::make_pair(trialResults[0].first, trialResults[0].second);
+  }
+
+  double geomeanNsec = 0.0;
+  for (size_t tryId = trialP25; tryId < trialP75; tryId++) {
+    geomeanNsec += std::log(trialResults[tryId].first);
+  }
+  geomeanNsec = std::exp(geomeanNsec / (1.0 * (trialP75 - trialP25)));
+
+  return std::make_pair(
+      geomeanNsec, trialResults[trialP25 + (trialP75 - trialP25) / 2].second);
+}
+
+static std::pair<double, UserCounters> runProfilingGetNSPerIteration(
+    const BenchmarkFun& fun, const double globalBaseline) {
+  using std::chrono::duration_cast;
+  using std::chrono::high_resolution_clock;
+  using std::chrono::nanoseconds;
+
+  // They key here is accuracy; too low numbers means the accuracy was
+  // coarse. We up the ante until we get to at least minNanoseconds
+  // timings.
+  static_assert(
+      std::is_same<high_resolution_clock::duration, nanoseconds>::value,
+      "High resolution clock must be nanosecond resolution.");
+
+  // This is a very simple measurement with a single epoch
+  // and should be used only for profiling purposes
+  detail::TimeIterData timeIterData = fun(FLAGS_bm_profile_iters);
+
+  auto nsecs = duration_cast<nanoseconds>(timeIterData.duration);
+  auto nsecIter = double(nsecs.count()) / timeIterData.niter - globalBaseline;
+  return std::make_pair(nsecIter, std::move(timeIterData.userCounters));
+}
+
+struct ScaleInfo {
+  double boundary;
+  const char* suffix;
+};
+
+static const ScaleInfo kTimeSuffixes[]{
+    {365.25 * 24 * 3600, "years"},
+    {24 * 3600, "days"},
+    {3600, "hr"},
+    {60, "min"},
+    {1, "s"},
+    {1E-3, "ms"},
+    {1E-6, "us"},
+    {1E-9, "ns"},
+    {1E-12, "ps"},
+    {1E-15, "fs"},
+    {0, nullptr},
+};
+
+static const ScaleInfo kMetricSuffixes[]{
+    {1E24, "Y"}, // yotta
+    {1E21, "Z"}, // zetta
+    {1E18, "X"}, // "exa" written with suffix 'X' so as to not create
+                 //   confusion with scientific notation
+    {1E15, "P"}, // peta
+    {1E12, "T"}, // terra
+    {1E9, "G"}, // giga
+    {1E6, "M"}, // mega
+    {1E3, "K"}, // kilo
+    {1, ""},
+    {1E-3, "m"}, // milli
+    {1E-6, "u"}, // micro
+    {1E-9, "n"}, // nano
+    {1E-12, "p"}, // pico
+    {1E-15, "f"}, // femto
+    {1E-18, "a"}, // atto
+    {1E-21, "z"}, // zepto
+    {1E-24, "y"}, // yocto
+    {0, nullptr},
+};
+
+static string humanReadable(
+    double n, unsigned int decimals, const ScaleInfo* scales) {
+  if (std::isinf(n) || std::isnan(n)) {
+    return folly::to<string>(n);
+  }
+
+  const double absValue = fabs(n);
+  const ScaleInfo* scale = scales;
+  while (absValue < scale[0].boundary && scale[1].suffix != nullptr) {
+    ++scale;
+  }
+
+  const double scaledValue = n / scale->boundary;
+  return stringPrintf("%.*f%s", decimals, scaledValue, scale->suffix);
+}
+
+static string readableTime(double n, unsigned int decimals) {
+  return humanReadable(n, decimals, kTimeSuffixes);
+}
+
+static string metricReadable(double n, unsigned int decimals) {
+  return humanReadable(n, decimals, kMetricSuffixes);
+}
+
+namespace {
+
+constexpr std::string_view kUnitHeaders = "relative  time/iter   iters/s";
+constexpr std::string_view kUnitHeadersPadding = "     ";
+void printHeaderContents(std::string_view file) {
+  printf(
+      "%-.*s%*s%*s",
+      static_cast<int>(file.size()),
+      file.data(),
+      static_cast<int>(kUnitHeadersPadding.size()),
+      kUnitHeadersPadding.data(),
+      static_cast<int>(kUnitHeaders.size()),
+      kUnitHeaders.data());
+}
+
+void printDefaultHeaderContents(std::string_view file, size_t columns) {
+  const size_t maxFileNameChars =
+      columns - kUnitHeaders.size() - kUnitHeadersPadding.size();
+
+  if (file.size() <= maxFileNameChars) {
+    printHeaderContents(file);
+  } else {
+    std::string truncatedFile = std::string(file.begin(), file.end());
+    constexpr std::string_view overflowFilePrefix = "[...]";
+    const auto overflow = truncatedFile.size() - maxFileNameChars;
+    truncatedFile.erase(0, overflow);
+    truncatedFile.replace(0, overflowFilePrefix.size(), overflowFilePrefix);
+    printHeaderContents(truncatedFile);
+  }
+}
+
+void printSeparator(char pad, unsigned int columns) {
+  puts(string(columns, pad).c_str());
+}
+
+class BenchmarkResultsPrinter {
+ public:
+  BenchmarkResultsPrinter() : columns_(FLAGS_bm_result_width_chars) {}
+  explicit BenchmarkResultsPrinter(std::set<std::string> counterNames)
+      : counterNames_(std::move(counterNames)),
+        namesLength_{std::accumulate(
+            counterNames_.begin(),
+            counterNames_.end(),
+            size_t{0},
+            [](size_t acc, auto&& name) { return acc + 2 + name.length(); })},
+        columns_(FLAGS_bm_result_width_chars + namesLength_) {}
+
+  void separator(char pad) { printSeparator(pad, columns_); }
+
+  void header(std::string_view file) {
+    separator('=');
+    printDefaultHeaderContents(file, columns_ - namesLength_);
+
+    for (auto const& name : counterNames_) {
+      printf("  %s", name.c_str());
+    }
+    printf("\n");
+    separator('=');
+  }
+
+  void print(const vector<detail::BenchmarkResult>& data) {
+    for (auto& datum : data) {
+      auto file = datum.file;
+      if (file != lastFile_) {
+        // New file starting
+        header(file);
+        lastFile_ = file;
+      }
+
+      string s = datum.name;
+      if (s == "-") {
+        // Simply draw a line across the benchmark results
+        separator('-');
+        continue;
+      }
+      if (s[0] == '"') {
+        // Simply print some text. Strips implied quote characters
+        // from the beginning and end of the name.
+        printf("%s\n", s.substr(1, s.length() - 2).c_str());
+        continue;
+      }
+      bool useBaseline = false;
+      // '%' indicates a relative benchmark.
+      if (s[0] == '%') {
+        s.erase(0, 1);
+        useBaseline = isBaselineSet();
+      } else {
+        baselineNsPerIter_ = datum.timeInNs;
+        useBaseline = false;
+      }
+      s.resize(columns_ - namesLength_ - kUnitHeaders.size(), ' ');
+
+      const auto nsPerIter = datum.timeInNs;
+      const auto secPerIter = nsPerIter / 1E9;
+      const auto itersPerSec = (secPerIter == 0)
+          ? std::numeric_limits<double>::infinity()
+          : (1 / secPerIter);
+      if (!useBaseline) {
+        // Print without baseline
+        printf(
+            "%*s%8.8s  %9.9s  %8.8s",
+            static_cast<int>(s.size()),
+            s.c_str(),
+            "", // Padding for "relative" header.
+            readableTime(secPerIter, 2).c_str(),
+            metricReadable(itersPerSec, 2).c_str());
+      } else {
+        // Print with baseline
+        const auto rel = baselineNsPerIter_ / nsPerIter * 100.0;
+        printf(
+            "%*s%#7.5g%%  %9.9s  %8.8s",
+            static_cast<int>(s.size()),
+            s.c_str(),
+            rel,
+            readableTime(secPerIter, 2).c_str(),
+            metricReadable(itersPerSec, 2).c_str());
+      }
+      for (auto const& name : counterNames_) {
+        if (auto ptr = folly::get_ptr(datum.counters, name)) {
+          switch (ptr->type) {
+            // UserMetrics constructed as precision_values avoid the
+            // implicit cast from long to double when formatting the output
+            case UserMetric::Type::TIME:
+              folly::variant_match(ptr->value, [&](auto value) {
+                printf(
+                    "  %*s",
+                    int(name.length()),
+                    readableTime(value, 2).c_str());
+              });
+              break;
+            case UserMetric::Type::METRIC:
+              folly::variant_match(ptr->value, [&](auto value) {
+                printf(
+                    "  %*s",
+                    int(name.length()),
+                    metricReadable(value, 2).c_str());
+              });
+              break;
+            case UserMetric::Type::CUSTOM:
+            default:
+              folly::variant_match(ptr->value, [&](auto value) {
+                printf(
+                    "  %*" PRId64,
+                    int(name.length()),
+                    static_cast<int64_t>(value));
+              });
+          }
+        } else {
+          printf("  %*s", int(name.length()), "NaN");
+        }
+      }
+      printf("\n");
+    }
+  }
+
+ private:
+  bool isBaselineSet() {
+    return baselineNsPerIter_ != numeric_limits<double>::max();
+  }
+
+  std::set<std::string> counterNames_;
+  size_t namesLength_{0};
+  size_t columns_{0};
+  double baselineNsPerIter_{numeric_limits<double>::max()};
+  string lastFile_;
+};
+} // namespace
+
+static void printBenchmarkResultsAsJson(
+    const vector<detail::BenchmarkResult>& data) {
+  dynamic d = dynamic::object;
+  for (auto& datum : data) {
+    d[datum.name] = datum.timeInNs * 1000.;
+  }
+
+  printf("%s\n", toPrettyJson(d).c_str());
+}
+
+void benchmarkResultsToDynamic(
+    const vector<detail::BenchmarkResult>& data, dynamic& out) {
+  out = dynamic::array;
+  for (auto& datum : data) {
+    if (!datum.counters.empty()) {
+      dynamic obj = dynamic::object;
+      for (auto& counter : datum.counters) {
+        dynamic counterInfo = dynamic::object;
+        folly::variant_match(counter.second.value, [&](auto value) {
+          counterInfo["value"] = value;
+        });
+        counterInfo["type"] = static_cast<int>(counter.second.type);
+        obj[counter.first] = counterInfo;
+      }
+      out.push_back(
+          dynamic::array(datum.file, datum.name, datum.timeInNs, obj));
+    } else {
+      out.push_back(dynamic::array(datum.file, datum.name, datum.timeInNs));
+    }
+  }
+}
+
+void benchmarkResultsFromDynamic(
+    const dynamic& d, vector<detail::BenchmarkResult>& results) {
+  for (auto& datum : d) {
+    results.push_back(
+        {datum[0].asString(),
+         datum[1].asString(),
+         datum[2].asDouble(),
+         UserCounters{}});
+  }
+}
+
+static pair<StringPiece, StringPiece> resultKey(
+    const detail::BenchmarkResult& result) {
+  return pair<StringPiece, StringPiece>(result.file, result.name);
+}
+
+void printResultComparison(
+    const vector<detail::BenchmarkResult>& base,
+    const vector<detail::BenchmarkResult>& test) {
+  map<pair<StringPiece, StringPiece>, double> baselines;
+
+  for (auto& baseResult : base) {
+    baselines[resultKey(baseResult)] = baseResult.timeInNs;
+  }
+
+  // Width available
+  const size_t columns = FLAGS_bm_result_width_chars;
+
+  auto header = [&](const string_view& file) {
+    printSeparator('=', columns);
+    printDefaultHeaderContents(file, columns);
+    printf("\n");
+    printSeparator('=', columns);
+  };
+
+  string lastFile;
+
+  for (auto& datum : test) {
+    folly::Optional<double> baseline =
+        folly::get_optional(baselines, resultKey(datum));
+    auto file = datum.file;
+    if (file != lastFile) {
+      // New file starting
+      header(file);
+      lastFile = file;
+    }
+
+    string s = datum.name;
+    if (s == "-") {
+      printSeparator('-', columns);
+      continue;
+    }
+    if (s[0] == '%') {
+      s.erase(0, 1);
+    }
+    s.resize(columns - 29, ' ');
+    auto nsPerIter = datum.timeInNs;
+    auto secPerIter = nsPerIter / 1E9;
+    auto itersPerSec = (secPerIter == 0)
+        ? std::numeric_limits<double>::infinity()
+        : (1 / secPerIter);
+    if (!baseline) {
+      // Print without baseline
+      printf(
+          "%*s           %9s  %7s\n",
+          static_cast<int>(s.size()),
+          s.c_str(),
+          readableTime(secPerIter, 2).c_str(),
+          metricReadable(itersPerSec, 2).c_str());
+    } else {
+      // Print with baseline
+      auto rel = *baseline / nsPerIter * 100.0;
+      printf(
+          "%*s %7.2f%%  %9s  %7s\n",
+          static_cast<int>(s.size()),
+          s.c_str(),
+          rel,
+          readableTime(secPerIter, 2).c_str(),
+          metricReadable(itersPerSec, 2).c_str());
+    }
+  }
+  printSeparator('=', columns);
+}
+
+void checkRunMode() {
+  if (folly::kIsDebug || folly::kIsSanitize) {
+    std::cerr << "WARNING: Benchmark running "
+              << (folly::kIsDebug ? "in DEBUG mode" : "with SANITIZERS")
+              << std::endl;
+  }
+}
+
+namespace {
+
+struct BenchmarksToRun {
+  const detail::BenchmarkRegistration* baseline = nullptr;
+  const detail::BenchmarkRegistration* suspenderBaseline = nullptr;
+  std::vector<const detail::BenchmarkRegistration*> benchmarks;
+  std::vector<size_t> separatorsAfter;
+};
+
+void addSeparator(BenchmarksToRun& res) {
+  size_t separatorAfter = res.benchmarks.size();
+  if (separatorAfter == 0) {
+    return;
+  }
+  if (res.separatorsAfter.empty() ||
+      res.separatorsAfter.back() != (separatorAfter - 1)) {
+    res.separatorsAfter.push_back(res.benchmarks.size() - 1);
+  }
+}
+
+BenchmarksToRun selectBenchmarksToRun(
+    const std::vector<detail::BenchmarkRegistration>& benchmarks) {
+  BenchmarksToRun res;
+
+  folly::Optional<boost::regex> bmRegex;
+  folly::Optional<boost::regex> bmFileRegex;
+
+  res.benchmarks.reserve(benchmarks.size());
+
+  if (!FLAGS_bm_regex.empty()) {
+    bmRegex.emplace(FLAGS_bm_regex);
+  }
+
+  if (!FLAGS_bm_file_regex.empty()) {
+    bmFileRegex.emplace(FLAGS_bm_file_regex);
+  }
+
+  for (auto& bm : benchmarks) {
+    if (bm.name == "-") {
+      addSeparator(res);
+      continue;
+    }
+
+    if (bm.name == kGlobalBenchmarkBaseline) {
+      res.baseline = &bm;
+      continue;
+    }
+
+    if (bm.name == kGlobalBenchmarkSuspenderBaseline) {
+      res.suspenderBaseline = &bm;
+      continue;
+    }
+
+    bool matchedName = !bmRegex || boost::regex_search(bm.name, *bmRegex);
+    bool matchedFile =
+        !bmFileRegex || boost::regex_search(bm.file, *bmFileRegex);
+
+    if (matchedName && matchedFile) {
+      res.benchmarks.push_back(&bm);
+    }
+  }
+
+  if (bmRegex) {
+    res.separatorsAfter.clear();
+  }
+
+  CHECK(res.baseline);
+
+  return res;
+}
+
+void maybeRunWarmUpIteration(const BenchmarksToRun& toRun) {
+  bool shouldRun = FLAGS_bm_warm_up_iteration;
+
+#if FOLLY_PERF_IS_SUPPORTED
+  shouldRun = shouldRun || !FLAGS_bm_perf_args.empty();
+#endif
+
+  if (!shouldRun) {
+    return;
+  }
+
+  for (const auto* bm : toRun.benchmarks) {
+    bm->func(1);
+  }
+}
+
+class ShouldDrawLineTracker {
+ public:
+  explicit ShouldDrawLineTracker(const BenchmarksToRun& toRun)
+      : separatorsAfter_(&toRun.separatorsAfter) {}
+
+  bool operator()() {
+    std::size_t i = curI_++;
+    if (drawAfterI_ >= separatorsAfter_->size()) {
+      return false;
+    }
+    std::size_t nextToDrawAfter = (*separatorsAfter_)[drawAfterI_];
+    if (i == nextToDrawAfter) {
+      ++drawAfterI_;
+      return true;
+    }
+    return false;
+  }
+
+ private:
+  const std::vector<std::size_t>* separatorsAfter_;
+  std::size_t curI_ = 0;
+  std::size_t drawAfterI_ = 0;
+};
+
+std::pair<std::set<std::string>, std::vector<detail::BenchmarkResult>>
+runBenchmarksWithPrinterImpl(
+    BenchmarkResultsPrinter* FOLLY_NULLABLE printer,
+    const BenchmarksToRun& toRun) {
+  vector<detail::BenchmarkResult> results;
+  results.reserve(toRun.benchmarks.size());
+
+  // PLEASE KEEP QUIET. MEASUREMENTS IN PROGRESS.
+
+  auto const globalBaseline =
+      runBenchmarkGetNSPerIteration(toRun.baseline->func, 0);
+
+  auto const globalSuspenderBaseline =
+      runBenchmarkGetNSPerIteration(toRun.suspenderBaseline->func, 0);
+
+  BenchmarkSuspender::suspenderOverhead = chrono::nanoseconds(
+      static_cast<chrono::high_resolution_clock::rep>(
+          globalSuspenderBaseline.first));
+
+  std::set<std::string> counterNames;
+  ShouldDrawLineTracker shouldDrawLineTracker(toRun);
+  for (std::size_t i = 0; i != toRun.benchmarks.size(); ++i) {
+    std::pair<double, UserCounters> elapsed;
+    const detail::BenchmarkRegistration& bm = *toRun.benchmarks[i];
+    bool shouldDrawLineAfter = shouldDrawLineTracker();
+
+    if (FLAGS_bm_profile) {
+      elapsed = runProfilingGetNSPerIteration(bm.func, globalBaseline.first);
+    } else {
+      elapsed = FLAGS_bm_estimate_time
+          ? runBenchmarkGetNSPerIterationEstimate(bm.func, globalBaseline.first)
+          : runBenchmarkGetNSPerIteration(bm.func, globalBaseline.first);
+    }
+
+    // if customized user counters is used, it cannot print the result in real
+    // time as it needs to run all cases first to know the complete set of
+    // counters have been used, then the header can be printed out properly
+    if (printer != nullptr) {
+      printer->print({{bm.file, bm.name, elapsed.first, elapsed.second}});
+      if (shouldDrawLineAfter) {
+        printer->separator('-');
+      }
+    }
+    results.push_back({bm.file, bm.name, elapsed.first, elapsed.second});
+
+    // get all counter names
+    for (auto const& kv : elapsed.second) {
+      counterNames.insert(kv.first);
+    }
+  }
+
+  // MEASUREMENTS DONE.
+
+  return std::make_pair(std::move(counterNames), std::move(results));
+}
+
+std::vector<detail::BenchmarkResult> resultsFromFile(
+    const std::string& filename) {
+  std::string content;
+  readFile(filename.c_str(), content);
+  std::vector<detail::BenchmarkResult> ret;
+  if (!content.empty()) {
+    benchmarkResultsFromDynamic(parseJson(content), ret);
+  }
+  return ret;
+}
+
+bool writeResultsToFile(
+    const std::vector<detail::BenchmarkResult>& results,
+    const std::string& filename) {
+  dynamic d;
+  benchmarkResultsToDynamic(results, d);
+  return writeFile(toPrettyJson(d), filename.c_str());
+}
+
+} // namespace
+
+namespace detail {
+
+std::ostream& operator<<(std::ostream& os, const BenchmarkResult& x) {
+  folly::dynamic r;
+  benchmarkResultsToDynamic({x}, r);
+  return os << r[0];
+}
+
+bool operator==(const BenchmarkResult& x, const BenchmarkResult& y) {
+  auto xtime = static_cast<std::uint64_t>(x.timeInNs * 1000);
+  auto ytime = static_cast<std::uint64_t>(y.timeInNs * 1000);
+  return x.name == y.name && x.file == y.file && xtime == ytime &&
+      x.counters == y.counters;
+}
+
+std::chrono::high_resolution_clock::duration BenchmarkSuspenderBase::timeSpent;
+std::chrono::high_resolution_clock::duration
+    BenchmarkSuspenderBase::suspenderOverhead;
+
+void BenchmarkingStateBase::addBenchmarkImpl(
+    std::string file, std::string name, BenchmarkFun fun, bool useCounter) {
+  std::lock_guard guard(mutex_);
+  benchmarks_.push_back(
+      {std::move(file), std::move(name), std::move(fun), useCounter});
+}
+
+bool BenchmarkingStateBase::useCounters() const {
+  std::lock_guard guard(mutex_);
+  return std::any_of(
+      benchmarks_.begin(), benchmarks_.end(), [](const auto& bm) {
+        return bm.useCounter;
+      });
+}
+
+std::vector<std::string> BenchmarkingStateBase::getBenchmarkList() {
+  std::vector<std::string> bmNames;
+  auto toRun = selectBenchmarksToRun(benchmarks_);
+  for (auto benchmarkRegistration : toRun.benchmarks) {
+    bmNames.push_back(benchmarkRegistration->name);
+  }
+
+  return bmNames;
+}
+
+// static
+folly::StringPiece BenchmarkingStateBase::getGlobalBaselineNameForTests() {
+  return kGlobalBenchmarkBaseline;
+}
+
+folly::StringPiece
+BenchmarkingStateBase::getGlobalSuspenderBaselineNameForTests() {
+  return kGlobalBenchmarkSuspenderBaseline;
+}
+
+PerfScoped BenchmarkingStateBase::doSetUpPerfScoped(
+    const std::vector<std::string>& args) const {
+  return PerfScoped{args};
+}
+
+PerfScoped BenchmarkingStateBase::setUpPerfScoped() const {
+  std::vector<std::string> perfArgs;
+#if FOLLY_PERF_IS_SUPPORTED
+  folly::split(' ', FLAGS_bm_perf_args, perfArgs, true);
+#endif
+  if (perfArgs.empty()) {
+    return PerfScoped{};
+  }
+  return doSetUpPerfScoped(perfArgs);
+}
+
+template <typename Printer>
+std::pair<std::set<std::string>, std::vector<BenchmarkResult>>
+BenchmarkingStateBase::runBenchmarksWithPrinter(Printer* printer) const {
+  std::lock_guard guard(mutex_);
+  BenchmarksToRun toRun = selectBenchmarksToRun(benchmarks_);
+  maybeRunWarmUpIteration(toRun);
+
+  detail::PerfScoped perf = setUpPerfScoped();
+  return runBenchmarksWithPrinterImpl(printer, toRun);
+}
+
+std::vector<BenchmarkResult> BenchmarkingStateBase::runBenchmarksWithResults()
+    const {
+  return runBenchmarksWithPrinter(
+             static_cast<BenchmarkResultsPrinter*>(nullptr))
+      .second;
+}
+
+std::vector<BenchmarkResult> runBenchmarksWithResults() {
+  return globalBenchmarkState().runBenchmarksWithResults();
+}
+
+} // namespace detail
+
+void runBenchmarks() {
+  auto& state = detail::globalBenchmarkState();
+
+  if (FLAGS_bm_list) {
+    auto bmNames = state.getBenchmarkList();
+    for (auto testName : bmNames) {
+      std::cout << testName << std::endl;
+    }
+    return;
+  }
+
+  if (FLAGS_bm_profile) {
+    printf(
+        "WARNING: Running with constant number of iterations. Results might be jittery.\n");
+  }
+
+  if (FLAGS_bm_min_iters >= FLAGS_bm_max_iters) {
+    std::cerr << "WARNING: bm_min_iters > bm_max_iters; increasing the max"
+              << std::endl;
+    FLAGS_bm_max_iters = FLAGS_bm_min_iters + 1;
+  }
+
+  checkRunMode();
+
+  BenchmarkResultsPrinter printer;
+  bool useCounter = state.useCounters();
+
+  // PLEASE KEEP QUIET. MEASUREMENTS IN PROGRESS.
+
+  const bool shouldPrintInline =
+      FLAGS_bm_relative_to.empty() && !FLAGS_json && !useCounter;
+  auto benchmarkResults =
+      state.runBenchmarksWithPrinter(shouldPrintInline ? &printer : nullptr);
+
+  // PLEASE MAKE NOISE. MEASUREMENTS DONE.
+
+  if (FLAGS_json) {
+    printBenchmarkResultsAsJson(benchmarkResults.second);
+  } else if (!FLAGS_bm_relative_to.empty()) {
+    printResultComparison(
+        resultsFromFile(FLAGS_bm_relative_to), benchmarkResults.second);
+  } else if (!shouldPrintInline) {
+    printer = BenchmarkResultsPrinter{std::move(benchmarkResults.first)};
+    printer.print(benchmarkResults.second);
+    printer.separator('=');
+  }
+
+  if (!FLAGS_bm_json_verbose.empty()) {
+    writeResultsToFile(benchmarkResults.second, FLAGS_bm_json_verbose);
+  }
+
+  checkRunMode();
+}
+
+} // namespace folly
diff --git a/folly/folly/Benchmark.h b/folly/folly/Benchmark.h
--- a/folly/folly/Benchmark.h
+++ b/folly/folly/Benchmark.h
@@ -33,6 +33,7 @@
 #include <limits>
 #include <mutex>
 #include <set>
+#include <string>
 #include <type_traits>
 #include <unordered_map>
 #include <variant>
@@ -42,6 +43,8 @@
 
 FOLLY_GFLAGS_DECLARE_bool(benchmark);
 FOLLY_GFLAGS_DECLARE_uint32(bm_result_width_chars);
+FOLLY_GFLAGS_DECLARE_int32(bm_min_iters);
+FOLLY_GFLAGS_DECLARE_int64(bm_max_iters);
 
 namespace folly {
 
@@ -215,7 +218,7 @@
   bool useCounters() const;
 
   void addBenchmarkImpl(
-      const char* file, StringPiece name, BenchmarkFun, bool useCounter);
+      std::string file, std::string name, BenchmarkFun, bool useCounter);
 
   std::vector<std::string> getBenchmarkList();
 
@@ -238,7 +241,7 @@
  public:
   template <typename Lambda>
   typename std::enable_if<folly::is_invocable_v<Lambda, unsigned>>::type
-  addBenchmark(const char* file, StringPiece name, Lambda&& lambda) {
+  addBenchmark(std::string file, std::string name, Lambda&& lambda) {
     auto execute = [=](unsigned int times) {
       BenchmarkSuspender<Clock>::timeSpent = {};
       unsigned int niter;
@@ -254,13 +257,14 @@
           UserCounters{}};
     };
 
-    this->addBenchmarkImpl(file, name, detail::BenchmarkFun(execute), false);
+    this->addBenchmarkImpl(
+        std::move(file), std::move(name), detail::BenchmarkFun(execute), false);
   }
 
   template <typename Lambda>
   typename std::enable_if<folly::is_invocable_v<Lambda>>::type addBenchmark(
-      const char* file, StringPiece name, Lambda&& lambda) {
-    addBenchmark(file, name, [=](unsigned int times) {
+      std::string file, std::string name, Lambda&& lambda) {
+    addBenchmark(std::move(file), std::move(name), [=](unsigned int times) {
       unsigned int niter = 0;
       while (times-- > 0) {
         niter += lambda();
@@ -272,7 +276,7 @@
   template <typename Lambda>
   typename std::enable_if<
       folly::is_invocable_v<Lambda, UserCounters&, unsigned>>::type
-  addBenchmark(const char* file, StringPiece name, Lambda&& lambda) {
+  addBenchmark(std::string file, std::string name, Lambda&& lambda) {
     auto execute = [=](unsigned int times) {
       BenchmarkSuspender<Clock>::timeSpent = {};
       unsigned int niter;
@@ -290,22 +294,25 @@
     };
 
     this->addBenchmarkImpl(
-        file,
-        name,
+        std::move(file),
+        std::move(name),
         std::function<detail::TimeIterData(unsigned int)>(execute),
         true);
   }
 
   template <typename Lambda>
   typename std::enable_if<folly::is_invocable_v<Lambda, UserCounters&>>::type
-  addBenchmark(const char* file, StringPiece name, Lambda&& lambda) {
-    addBenchmark(file, name, [=](UserCounters& counters, unsigned int times) {
-      unsigned int niter = 0;
-      while (times-- > 0) {
-        niter += lambda(counters);
-      }
-      return niter;
-    });
+  addBenchmark(std::string file, std::string name, Lambda&& lambda) {
+    addBenchmark(
+        std::move(file),
+        std::move(name),
+        [=](UserCounters& counters, unsigned int times) {
+          unsigned int niter = 0;
+          while (times-- > 0) {
+            niter += lambda(counters);
+          }
+          return niter;
+        });
   }
 };
 
@@ -323,8 +330,9 @@
  * is not.
  */
 inline void addBenchmarkImpl(
-    const char* file, StringPiece name, BenchmarkFun f, bool useCounter) {
-  globalBenchmarkState().addBenchmarkImpl(file, name, std::move(f), useCounter);
+    std::string file, std::string name, BenchmarkFun f, bool useCounter) {
+  globalBenchmarkState().addBenchmarkImpl(
+      std::move(file), std::move(name), std::move(f), useCounter);
 }
 
 } // namespace detail
@@ -350,8 +358,9 @@
  *    as their first parameter.
  */
 template <typename Lambda>
-void addBenchmark(const char* file, StringPiece name, Lambda&& lambda) {
-  detail::globalBenchmarkState().addBenchmark(file, name, lambda);
+void addBenchmark(std::string file, std::string name, Lambda&& lambda) {
+  detail::globalBenchmarkState().addBenchmark(
+      std::move(file), std::move(name), lambda);
 }
 
 struct dynamic;
@@ -373,24 +382,29 @@
  * friends below.
  */
 
-#define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName)        \
-  static void funName(paramType);                                            \
-  [[maybe_unused]] static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \
-      (::folly::addBenchmark(                                                \
-           __FILE__,                                                         \
-           stringName,                                                       \
-           [](paramType paramName) -> unsigned {                             \
-             funName(paramName);                                             \
-             return rv;                                                      \
-           }),                                                               \
-       true);                                                                \
+FOLLY_PUSH_WARNING
+FOLLY_CLANG_DISABLE_WARNING("-Wglobal-constructors")
+
+#define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName) \
+  static void funName(paramType);                                     \
+  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(           \
+      follyBenchmarkUnused) =                                         \
+      (::folly::addBenchmark(                                         \
+           __FILE__,                                                  \
+           stringName,                                                \
+           [](paramType paramName) -> unsigned {                      \
+             funName(paramName);                                      \
+             return rv;                                               \
+           }),                                                        \
+       true);                                                         \
   static void funName(paramType paramName)
 
 #define BENCHMARK_IMPL_COUNTERS(                                             \
     funName, stringName, counters, rv, paramType, paramName)                 \
   static void funName(                                                       \
       ::folly::UserCounters& FOLLY_PP_DETAIL_APPEND_VA_ARG(paramType));      \
-  [[maybe_unused]] static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \
+  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(                  \
+      follyBenchmarkUnused) =                                                \
       (::folly::addBenchmark(                                                \
            __FILE__,                                                         \
            stringName,                                                       \
@@ -409,16 +423,19 @@
  * number of iterations. Used internally, see BENCHMARK_MULTI and friends
  * below.
  */
-#define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName)      \
-  static unsigned funName(paramType);                                        \
-  [[maybe_unused]] static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \
-      (::folly::addBenchmark(                                                \
-           __FILE__,                                                         \
-           stringName,                                                       \
-           [](paramType paramName) { return funName(paramName); }),          \
-       true);                                                                \
+#define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName) \
+  static unsigned funName(paramType);                                   \
+  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(             \
+      follyBenchmarkUnused) =                                           \
+      (::folly::addBenchmark(                                           \
+           __FILE__,                                                    \
+           stringName,                                                  \
+           [](paramType paramName) { return funName(paramName); }),     \
+       true);                                                           \
   static unsigned funName(paramType paramName)
 
+FOLLY_POP_WARNING
+
 /**
  * Introduces a benchmark function. Use with either one or two arguments.
  * The first is the name of the benchmark. Use something descriptive, such
@@ -661,7 +678,8 @@
  * Draws a line of dashes.
  */
 #define BENCHMARK_DRAW_LINE()                                                \
-  [[maybe_unused]] static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \
+  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(                  \
+      follyBenchmarkUnused) =                                                \
       (::folly::addBenchmark(__FILE__, "-", []() -> unsigned { return 0; }), \
        true)
 
@@ -669,12 +687,13 @@
  * Prints arbitrary text.
  */
 #define BENCHMARK_DRAW_TEXT(text)                                              \
-  [[maybe_unused]] static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) =   \
+  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(                    \
+      follyBenchmarkUnused) =                                                  \
       (::folly::addBenchmark(__FILE__, #text, []() -> unsigned { return 0; }), \
        true)
 
 /**
- * Allows execution of code that doesn't count torward the benchmark's
+ * Allows execution of code that doesn't count toward the benchmark's
  * time budget. Example:
  *
  * BENCHMARK_START_GROUP(insertVectorBegin, n) {
diff --git a/folly/folly/BenchmarkUtil.cpp b/folly/folly/BenchmarkUtil.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/BenchmarkUtil.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/BenchmarkUtil.h>
+
+#include <array> // @manual
+#include <filesystem>
+#include <fstream>
+#include <string>
+
+#include <fmt/format.h>
+
+#include <folly/lang/Align.h>
+#include <folly/portability/Sched.h>
+#include <folly/portability/Windows.h>
+#include <folly/system/arch/x86.h>
+
+#if defined(__APPLE__) || defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#endif
+
+namespace folly {
+
+namespace detail {
+
+size_t bm_llc_size_fallback() {
+#if defined(__linux__)
+
+  /// sysctl is deprecated on Linux; walk sysfs
+
+  int cpu = sched_getcpu();
+  if (cpu < 0) {
+    return 0;
+  }
+
+  auto dir = std::filesystem::path(
+      fmt::format("/sys/devices/system/cpu/cpu{}/cache", cpu));
+
+  size_t size = 0;
+  for (const auto& entry : std::filesystem::directory_iterator(dir)) {
+    if (entry.path().filename().native().starts_with("index")) {
+      std::ifstream size_ifs(entry.path() / "size");
+      std::string size_str;
+      size_ifs >> size_str;
+      size_t size_num = std::stoul(size_str);
+      if (size_str.back() == 'K') {
+        size_num *= 1024;
+      } else if (size_str.back() == 'M') {
+        size_num *= 1024 * 1024;
+      }
+      size = std::max(size, size_num);
+    }
+  }
+
+  return size;
+
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+
+  auto names = std::array{
+      "hw.l3cachesize", // FreeBSD, Intel Mac
+      "hw.l2cachesize", // Apple Silicon
+  };
+  for (auto name : names) {
+    uint64_t size = 0;
+    size_t len = sizeof(size);
+    if (sysctlbyname(name, &size, &len, NULL, 0) == 0 && size > 0) {
+      return size;
+    }
+  }
+
+  return 0;
+
+#elif defined(_WIN32)
+
+  DWORD length = 0;
+  GetLogicalProcessorInformation(nullptr, &length);
+  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || length == 0) {
+    return 0;
+  }
+  if (length % sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) != 0) {
+    return 0;
+  }
+
+  size_t const count = length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
+  std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> infos(count);
+  if (!GetLogicalProcessorInformation(infos.data(), &length)) {
+    return 0;
+  }
+
+  size_t size = 0;
+  for (auto const& info : infos) {
+    if (info.Relationship == RelationCache) {
+      size = std::max<size_t>(size, info.Cache.Size);
+    }
+  }
+  return size;
+
+#else
+
+  return 0;
+
+#endif
+}
+
+} // namespace detail
+
+size_t bm_llc_size() {
+  return kIsArchX86 || kIsArchAmd64
+      ? folly::x86_cpuid_get_llc_cache_info().cache_size()
+      : detail::bm_llc_size_fallback();
+}
+
+void bm_llc_evict(size_t value) {
+  constexpr auto step =
+      folly::hardware_constructive_interference_size / sizeof(size_t);
+
+  constexpr auto words_per_line =
+      folly::hardware_constructive_interference_size / step;
+
+  static auto const llc_size = bm_llc_size();
+
+  auto const llc_lines =
+      llc_size / folly::hardware_constructive_interference_size;
+
+  static std::unique_ptr<size_t volatile[]> const vec{
+      new size_t[llc_lines * words_per_line]};
+
+  // do N passes over the region, each time touching one word per line
+  for (size_t j = 0; j < step; ++j) {
+    for (size_t i = 0; i < llc_lines; i += step) {
+      vec[i * words_per_line + j] = value;
+    }
+  }
+}
+
+} // namespace folly
diff --git a/folly/folly/BenchmarkUtil.h b/folly/folly/BenchmarkUtil.h
--- a/folly/folly/BenchmarkUtil.h
+++ b/folly/folly/BenchmarkUtil.h
@@ -44,4 +44,19 @@
   compiler_must_not_predict(datum);
 }
 
+namespace detail {
+size_t bm_llc_size_fallback();
+}
+
+/// bm_llc_size
+///
+/// Calculates the size of the LLC (Last Level Cache, typically L3 on x86-64).
+size_t bm_llc_size();
+
+/// bm_llc_evict
+///
+/// Write to a large block of memory and evict whatever cache lines might
+/// currently be resident in any levels of cache.
+void bm_llc_evict(size_t value);
+
 } // namespace folly
diff --git a/folly/folly/CPortability.h b/folly/folly/CPortability.h
--- a/folly/folly/CPortability.h
+++ b/folly/folly/CPortability.h
@@ -47,6 +47,14 @@
 #endif
 #endif
 
+/// FOLLY_GLIBC_PREREQ
+#if !defined(__GLIBC__)
+#define FOLLY_GLIBC_PREREQ(maj, min) 0
+#else
+#define FOLLY_GLIBC_PREREQ(maj, min) \
+  (__GLIBC__ > (maj)) || (__GLIBC__ == (maj) && __GLIBC_MINOR__ >= (min))
+#endif
+
 #if defined(__has_builtin)
 #define FOLLY_HAS_BUILTIN(...) __has_builtin(__VA_ARGS__)
 #else
diff --git a/folly/folly/CancellationToken-inl.h b/folly/folly/CancellationToken-inl.h
--- a/folly/folly/CancellationToken-inl.h
+++ b/folly/folly/CancellationToken-inl.h
@@ -32,7 +32,7 @@
 // Internal cancellation state object.
 class CancellationState {
  public:
-  FOLLY_NODISCARD static CancellationStateSourcePtr create();
+  [[nodiscard]] static CancellationStateSourcePtr create();
 
  protected:
   // Constructed initially with a CancellationSource reference count of 1.
@@ -49,9 +49,9 @@
   void removeSourceReference() noexcept;
 
  public:
-  FOLLY_NODISCARD CancellationStateTokenPtr addTokenReference() noexcept;
+  [[nodiscard]] CancellationStateTokenPtr addTokenReference() noexcept;
 
-  FOLLY_NODISCARD CancellationStateSourcePtr addSourceReference() noexcept;
+  [[nodiscard]] CancellationStateSourcePtr addSourceReference() noexcept;
 
   bool tryAddCallback(
       CancellationCallback* callback,
@@ -112,7 +112,7 @@
 
  public:
   template <typename... Args>
-  FOLLY_NODISCARD static std::
+  [[nodiscard]] static std::
       pair<CancellationStateSourcePtr, std::tuple<Data...>*>
       create(Args&&... data);
 
@@ -442,7 +442,8 @@
 }
 
 template <typename... Ts>
-inline CancellationToken CancellationToken::merge(Ts&&... tokens) {
+CancellationToken cancellation_token_merge_fn::operator()(
+    Ts&&... tokens) const {
   if constexpr (sizeof...(Ts) == 0) {
     return CancellationToken();
   } else if constexpr (sizeof...(Ts) == 1) {
@@ -489,14 +490,17 @@
       }
       return *copyToks[0];
     } else if constexpr (NCopy == N) {
-      return CancellationToken(detail::MergingCancellationState::createCopy(
-          copyIdx, copyToks.data()));
+      return CancellationToken(
+          detail::MergingCancellationState::createCopy(
+              copyIdx, copyToks.data()));
     } else if constexpr (NCopy == 0) {
-      return CancellationToken(detail::MergingCancellationState::createMove(
-          moveIdx, moveToks.data()));
+      return CancellationToken(
+          detail::MergingCancellationState::createMove(
+              moveIdx, moveToks.data()));
     } else {
-      return CancellationToken(detail::MergingCancellationState::createCopyMove(
-          copyIdx, copyToks.data(), moveIdx, moveToks.data()));
+      return CancellationToken(
+          detail::MergingCancellationState::createCopyMove(
+              copyIdx, copyToks.data(), moveIdx, moveToks.data()));
     }
   }
 }
diff --git a/folly/folly/CancellationToken.h b/folly/folly/CancellationToken.h
--- a/folly/folly/CancellationToken.h
+++ b/folly/folly/CancellationToken.h
@@ -29,6 +29,7 @@
 
 class CancellationCallback;
 class CancellationSource;
+struct cancellation_token_merge_fn;
 
 namespace detail {
 class CancellationState;
@@ -107,18 +108,6 @@
   bool canBeCancelled() const noexcept;
 
   /**
-   * Obtain a CancellationToken linked to any number of other
-   * CancellationTokens.
-   *
-   * This token will have cancellation requested when any of the passed-in
-   * tokens do.
-   * This token is cancellable if any of the passed-in tokens are at the time of
-   * construction.
-   */
-  template <typename... Ts>
-  static CancellationToken merge(Ts&&... tokens);
-
-  /**
    * Swaps the underlying state of the cancellation token with the token that is
    * passed-in.
    */
@@ -130,6 +119,7 @@
  private:
   friend class CancellationCallback;
   friend class CancellationSource;
+  friend struct cancellation_token_merge_fn;
 
   explicit CancellationToken(detail::CancellationStateTokenPtr state) noexcept;
 
@@ -355,6 +345,25 @@
   // thread and it is now safe to exit the destructor.
   std::atomic<bool> callbackCompleted_;
 };
+
+/**
+ * Obtain a CancellationToken linked to any number of other
+ * CancellationTokens.
+ *
+ * This token will have cancellation requested when any of the passed-in
+ * tokens do.
+ * This token is cancellable if any of the passed-in tokens are at the time of
+ * construction.
+ *
+ * Example:
+ *   CancellationSource a,b;
+ *   auto c = cancellation_token_merge(a.getToken(), b.getToken());
+ */
+struct cancellation_token_merge_fn {
+  template <typename... Ts>
+  CancellationToken operator()(Ts&&... tokens) const;
+};
+inline constexpr cancellation_token_merge_fn cancellation_token_merge{};
 
 } // namespace folly
 
diff --git a/folly/folly/ClockGettimeWrappers.cpp b/folly/folly/ClockGettimeWrappers.cpp
--- a/folly/folly/ClockGettimeWrappers.cpp
+++ b/folly/folly/ClockGettimeWrappers.cpp
@@ -68,7 +68,7 @@
     }
     p = dlsym(m_handle, "__vdso_clock_gettime_ns");
     if (p) {
-      folly::chrono::clock_gettime_ns = (int64_t(*)(clockid_t))p;
+      folly::chrono::clock_gettime_ns = (int64_t (*)(clockid_t))p;
     }
   }
 
diff --git a/folly/folly/ConcurrentBitSet.h b/folly/folly/ConcurrentBitSet.h
--- a/folly/folly/ConcurrentBitSet.h
+++ b/folly/folly/ConcurrentBitSet.h
@@ -95,14 +95,14 @@
  private:
   // Pick the largest lock-free type available
 #if (ATOMIC_LLONG_LOCK_FREE == 2)
-  typedef unsigned long long BlockType;
+  using BlockType = unsigned long long;
 #elif (ATOMIC_LONG_LOCK_FREE == 2)
   typedef unsigned long BlockType;
 #else
   // Even if not lock free, what can we do?
   typedef unsigned int BlockType;
 #endif
-  typedef std::atomic<BlockType> AtomicBlockType;
+  using AtomicBlockType = std::atomic<BlockType>;
 
   static constexpr size_t kBitsPerBlock =
       std::numeric_limits<BlockType>::digits;
diff --git a/folly/folly/ConcurrentSkipList-inl.h b/folly/folly/ConcurrentSkipList-inl.h
--- a/folly/folly/ConcurrentSkipList-inl.h
+++ b/folly/folly/ConcurrentSkipList-inl.h
@@ -48,7 +48,7 @@
   };
 
  public:
-  typedef T value_type;
+  using value_type = T;
 
   SkipListNode(const SkipListNode&) = delete;
   SkipListNode& operator=(const SkipListNode&) = delete;
diff --git a/folly/folly/ConcurrentSkipList.h b/folly/folly/ConcurrentSkipList.h
--- a/folly/folly/ConcurrentSkipList.h
+++ b/folly/folly/ConcurrentSkipList.h
@@ -147,16 +147,16 @@
   static_assert(
       MAX_HEIGHT >= 2 && MAX_HEIGHT < 64,
       "MAX_HEIGHT can only be in the range of [2, 64)");
-  typedef std::unique_lock<folly::MicroSpinLock> ScopedLocker;
-  typedef ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT> SkipListType;
+  using ScopedLocker = std::unique_lock<folly::MicroSpinLock>;
+  using SkipListType = ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>;
 
  public:
-  typedef detail::SkipListNode<T> NodeType;
-  typedef T value_type;
-  typedef T key_type;
+  using NodeType = detail::SkipListNode<T>;
+  using value_type = T;
+  using key_type = T;
 
-  typedef detail::csl_iterator<value_type, NodeType> iterator;
-  typedef detail::csl_iterator<const value_type, NodeType> const_iterator;
+  using iterator = detail::csl_iterator<value_type, NodeType>;
+  using const_iterator = detail::csl_iterator<const value_type, NodeType>;
 
   class Accessor;
   class Skipper;
@@ -535,23 +535,23 @@
 
 template <typename T, typename Comp, typename NodeAlloc, int MAX_HEIGHT>
 class ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Accessor {
-  typedef detail::SkipListNode<T> NodeType;
-  typedef ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT> SkipListType;
+  using NodeType = detail::SkipListNode<T>;
+  using SkipListType = ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>;
 
  public:
-  typedef T value_type;
-  typedef T key_type;
-  typedef T& reference;
-  typedef T* pointer;
-  typedef const T& const_reference;
-  typedef const T* const_pointer;
-  typedef size_t size_type;
-  typedef Comp key_compare;
-  typedef Comp value_compare;
+  using value_type = T;
+  using key_type = T;
+  using reference = T&;
+  using pointer = T*;
+  using const_reference = const T&;
+  using const_pointer = const T*;
+  using size_type = size_t;
+  using key_compare = Comp;
+  using value_compare = Comp;
 
-  typedef typename SkipListType::iterator iterator;
-  typedef typename SkipListType::const_iterator const_iterator;
-  typedef typename SkipListType::Skipper Skipper;
+  using iterator = typename SkipListType::iterator;
+  using const_iterator = typename SkipListType::const_iterator;
+  using Skipper = typename SkipListType::Skipper;
 
   explicit Accessor(std::shared_ptr<ConcurrentSkipList> skip_list)
       : slHolder_(std::move(skip_list)) {
@@ -673,10 +673,10 @@
           ValT,
           std::forward_iterator_tag> {
  public:
-  typedef ValT value_type;
-  typedef value_type& reference;
-  typedef value_type* pointer;
-  typedef ptrdiff_t difference_type;
+  using value_type = ValT;
+  using reference = value_type&;
+  using pointer = value_type*;
+  using difference_type = ptrdiff_t;
 
   explicit csl_iterator(NodeT* node = nullptr) : node_(node) {}
 
@@ -711,15 +711,15 @@
 // Skipper interface
 template <typename T, typename Comp, typename NodeAlloc, int MAX_HEIGHT>
 class ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Skipper {
-  typedef detail::SkipListNode<T> NodeType;
-  typedef ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT> SkipListType;
-  typedef typename SkipListType::Accessor Accessor;
+  using NodeType = detail::SkipListNode<T>;
+  using SkipListType = ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>;
+  using Accessor = typename SkipListType::Accessor;
 
  public:
-  typedef T value_type;
-  typedef T& reference;
-  typedef T* pointer;
-  typedef ptrdiff_t difference_type;
+  using value_type = T;
+  using reference = T&;
+  using pointer = T*;
+  using difference_type = ptrdiff_t;
 
   Skipper(std::shared_ptr<SkipListType> skipList)
       : accessor_(std::move(skipList)) {
@@ -779,7 +779,7 @@
     return succs_[0]->data();
   }
 
-  value_type* operator->() {
+  value_type* operator->() const {
     DCHECK(succs_[0] != nullptr);
     return &succs_[0]->data();
   }
diff --git a/folly/folly/ConstexprMath.h b/folly/folly/ConstexprMath.h
--- a/folly/folly/ConstexprMath.h
+++ b/folly/folly/ConstexprMath.h
@@ -417,7 +417,7 @@
 
 /// constexpr_mult
 ///
-/// Multiply two values, allowing for constexpr floating-pooint overflow to
+/// Multiply two values, allowing for constexpr floating-point overflow to
 /// infinity.
 template <typename T>
 constexpr T constexpr_mult(T const a, T const b) {
@@ -513,7 +513,7 @@
 /// constant evaluations. Like std::exp, which becomes constexpr in C++26.
 ///
 /// The integer overload uses iterated squaring and multiplication. The
-/// floating-point overlaod naively evaluates the taylor series of exp(num)
+/// floating-point overload naively evaluates the taylor series of exp(num)
 /// until approximate convergence.
 ///
 /// mimic: std::exp (C++23, C++26)
@@ -875,7 +875,7 @@
       // If Src is larger in size, we need to clamp it to valid range in Dst.
       Dst(constexpr_clamp(src, Src(L::min()), Src(L::max()))))
     // Src and Dst have different signedness.
-    // Check if it's signed -> unsigend cast.
+    // Check if it's signed -> unsigned cast.
     : std::is_signed<Src>::value && std::is_unsigned<Dst>::value
     ? (
       // If src < 0, the result should be 0.
diff --git a/folly/folly/Conv.h b/folly/folly/Conv.h
--- a/folly/folly/Conv.h
+++ b/folly/folly/Conv.h
@@ -511,7 +511,7 @@
 
 template <class Tgt>
 void toAppend(__int128 value, Tgt* result) {
-  typedef unsigned __int128 Usrc;
+  using Usrc = unsigned __int128;
   char buffer[detail::digitsEnough<unsigned __int128>() + 1];
   const auto oute = buffer + sizeof(buffer);
   size_t p;
@@ -613,8 +613,8 @@
 typename std::enable_if<
     is_integral_v<Src> && IsSomeString<Tgt>::value && sizeof(Src) < 4>::type
 toAppend(Src value, Tgt* result) {
-  typedef typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type
-      Intermediate;
+  using Intermediate =
+      typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type;
   toAppend<Tgt>(static_cast<Intermediate>(value), result);
 }
 
@@ -623,8 +623,8 @@
     is_integral_v<Src> && sizeof(Src) < 4 && !std::is_same<Src, char>::value,
     size_t>::type
 estimateSpaceNeeded(Src value) {
-  typedef typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type
-      Intermediate;
+  using Intermediate =
+      typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type;
   return estimateSpaceNeeded(static_cast<Intermediate>(value));
 }
 
@@ -651,7 +651,7 @@
 /// Operating mode for the floating point type version of
 /// `folly::ToAppend`. This is modeled after
 /// `double_conversion::DoubleToStringConverter::DtoaMode`.
-/// Dtoa is an acryonym for Double to ASCII.
+/// Dtoa is an acronym for Double to ASCII.
 enum class DtoaMode {
   /// Outputs the shortest representation of a `double`.
   /// The output is either in decimal or exponential notation; which ever is
@@ -669,14 +669,14 @@
   /// Outputs with a precision that is independent of the decimal point.
   /// The outputs is either decimal or exponential notation, depending on the
   /// value and the precision.
-  /// Similar to `printf`'s %g formating.
+  /// Similar to `printf`'s %g formatting.
   /// Use the `numDigits` parameter to specify the precision.
   PRECISION,
 };
 
 /// Flags for the floating point type version of `folly::ToAppend`.
 /// This is modeled after `double_conversion::DoubleToStringConverter::Flags`.
-/// Dtoa is an acryonym for Double to ASCII.
+/// Dtoa is an acronym for Double to ASCII.
 /// This enum is used to store bit wise flags, so a variable of this type may be
 /// a bitwise combination of these definitions.
 enum class DtoaFlags {
@@ -737,7 +737,7 @@
       return double_conversion::DoubleToStringConverter::PRECISION;
     default: /* unexpected */
       assert(false);
-      // Default to PRECISION per exising behavior.
+      // Default to PRECISION per existing behavior.
       return double_conversion::DoubleToStringConverter::PRECISION;
   }
 }
@@ -1256,9 +1256,9 @@
 #endif
 
 template <typename T>
-typename std::
-    enable_if<std::is_same<T, bool>::value, Expected<T, ConversionCode>>::type
-    convertTo(StringPiece* src) noexcept {
+typename std::enable_if<
+    std::is_same<T, bool>::value,
+    Expected<T, ConversionCode>>::type convertTo(StringPiece* src) noexcept {
   return str_to_bool(src);
 }
 
@@ -1310,7 +1310,7 @@
  * Parsing strings to numeric types.
  */
 template <typename Tgt>
-FOLLY_NODISCARD inline typename std::enable_if< //
+[[nodiscard]] inline typename std::enable_if< //
     is_arithmetic_v<Tgt>,
     Expected<StringPiece, ConversionCode>>::type
 parseTo(StringPiece src, Tgt& out) {
@@ -1513,7 +1513,7 @@
  * }
  */
 template <class T>
-FOLLY_NODISCARD typename std::enable_if<
+[[nodiscard]] typename std::enable_if<
     std::is_enum<T>::value,
     Expected<StringPiece, ConversionCode>>::type
 parseTo(StringPiece in, T& out) noexcept {
@@ -1523,7 +1523,7 @@
   return restOrError;
 }
 
-FOLLY_NODISCARD
+[[nodiscard]]
 inline Expected<StringPiece, ConversionCode> parseTo(
     StringPiece in, StringPiece& out) noexcept {
   out = in;
@@ -1542,27 +1542,27 @@
 
 } // namespace detail
 
-FOLLY_NODISCARD
+[[nodiscard]]
 inline Expected<StringPiece, ConversionCode> parseTo(
     StringPiece in, std::string& out) {
   return detail::parseToStr(in, out);
 }
 
-FOLLY_NODISCARD
+[[nodiscard]]
 inline Expected<StringPiece, ConversionCode> parseTo(
     StringPiece in, std::string_view& out) {
   out = std::string_view(in.data(), in.size());
   return StringPiece{in.end(), in.end()};
 }
 
-FOLLY_NODISCARD
+[[nodiscard]]
 inline Expected<StringPiece, ConversionCode> parseTo(
     StringPiece in, fbstring& out) {
   return detail::parseToStr(in, out);
 }
 
 template <class Str>
-FOLLY_NODISCARD inline typename std::enable_if<
+[[nodiscard]] inline typename std::enable_if<
     IsSomeString<Str>::value,
     Expected<StringPiece, ConversionCode>>::type
 parseTo(StringPiece in, Str& out) {
diff --git a/folly/folly/CppAttributes.h b/folly/folly/CppAttributes.h
--- a/folly/folly/CppAttributes.h
+++ b/folly/folly/CppAttributes.h
@@ -177,6 +177,12 @@
 #define FOLLY_ATTR_GNU_RETAIN
 #endif
 
+#if FOLLY_HAS_CPP_ATTRIBUTE(gnu::noclone)
+#define FOLLY_ATTR_GNU_NOCLONE gnu::noclone
+#else
+#define FOLLY_ATTR_GNU_NOCLONE
+#endif
+
 #if FOLLY_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
 #define FOLLY_ATTR_CLANG_LIFETIMEBOUND clang::lifetimebound
 #else
diff --git a/folly/folly/CpuId.h b/folly/folly/CpuId.h
--- a/folly/folly/CpuId.h
+++ b/folly/folly/CpuId.h
@@ -20,10 +20,7 @@
 #include <cstring>
 
 #include <folly/Portability.h>
-
-#ifdef _MSC_VER
-#include <intrin.h>
-#endif
+#include <folly/system/arch/x86.h>
 
 namespace folly {
 
@@ -42,116 +39,23 @@
   // mode since inlining happens more likely, and it doesn't happen for
   // statically linked binaries which don't depend on the PLT)
   FOLLY_ALWAYS_INLINE CpuId() {
-#if defined(_MSC_VER) && (FOLLY_X64 || defined(_M_IX86))
-#if !defined(__clang__)
-    int reg[4];
-    __cpuid(static_cast<int*>(reg), 0);
-    vendor_[0] = (uint32_t)reg[1];
-    vendor_[1] = (uint32_t)reg[3];
-    vendor_[2] = (uint32_t)reg[2];
+    unsigned int reg[4];
+    x86_cpuid(reg, 0);
+    vendor_[0] = reg[1];
+    vendor_[1] = reg[3];
+    vendor_[2] = reg[2];
     const int n = reg[0];
     if (n >= 1) {
-      __cpuid(static_cast<int*>(reg), 1);
-      f1c_ = uint32_t(reg[2]);
-      f1d_ = uint32_t(reg[3]);
-    }
-    if (n >= 7) {
-      __cpuidex(static_cast<int*>(reg), 7, 0);
-      f7b_ = uint32_t(reg[1]);
-      f7c_ = uint32_t(reg[2]);
-      f7d_ = uint32_t(reg[3]);
-    }
-#else
-    // Clang compiler has a bug (fixed in https://reviews.llvm.org/D101338) in
-    // which the `__cpuid` intrinsic does not save and restore `rbx` as it needs
-    // to due to being a reserved register. So in that case, do the `cpuid`
-    // ourselves. Clang supports inline assembly anyway.
-    uint32_t n;
-    uint32_t v0b, v0d, v0c;
-    __asm__(
-        "pushq %%rbx\n\t"
-        "cpuid\n\t"
-        "movl %%ebx, %1\n\t"
-        "popq %%rbx\n\t"
-        : "=a"(n), "=r"(v0b), "=d"(v0d), "=c"(v0c)
-        : "a"(0));
-    vendor_[0] = v0b;
-    vendor_[1] = v0d;
-    vendor_[2] = v0c;
-    if (n >= 1) {
-      uint32_t f1a;
-      __asm__(
-          "pushq %%rbx\n\t"
-          "cpuid\n\t"
-          "popq %%rbx\n\t"
-          : "=a"(f1a), "=c"(f1c_), "=d"(f1d_)
-          : "a"(1)
-          :);
-    }
-    if (n >= 7) {
-      __asm__(
-          "pushq %%rbx\n\t"
-          "cpuid\n\t"
-          "movq %%rbx, %%rax\n\t"
-          "popq %%rbx"
-          : "=a"(f7b_), "=c"(f7c_), "=d"(f7d_)
-          : "a"(7), "c"(0));
-    }
-#endif
-#elif defined(__i386__) && defined(__PIC__) && !defined(__clang__) && \
-    defined(__GNUC__)
-    // The following block like the normal cpuid branch below, but gcc
-    // reserves ebx for use of its pic register so we must specially
-    // handle the save and restore to avoid clobbering the register
-    uint32_t n;
-    uint32_t v0b, v0d, v0c;
-    __asm__(
-        "pushl %%ebx\n\t"
-        "cpuid\n\t"
-        "movl %%ebx, %1\n\t"
-        "popl %%ebx\n\t"
-        : "=a"(n), "=r"(v0b), "=d"(v0d), "=c"(v0c)
-        : "a"(0));
-    vendor_[0] = v0b;
-    vendor_[1] = v0d;
-    vendor_[2] = v0c;
-    if (n >= 1) {
-      uint32_t f1a;
-      __asm__(
-          "pushl %%ebx\n\t"
-          "cpuid\n\t"
-          "popl %%ebx\n\t"
-          : "=a"(f1a), "=c"(f1c_), "=d"(f1d_)
-          : "a"(1)
-          :);
-    }
-    if (n >= 7) {
-      __asm__(
-          "pushl %%ebx\n\t"
-          "cpuid\n\t"
-          "movl %%ebx, %%eax\n\t"
-          "popl %%ebx"
-          : "=a"(f7b_), "=c"(f7c_), "=d"(f7d_)
-          : "a"(7), "c"(0));
-    }
-#elif FOLLY_X64 || defined(__i386__)
-    uint32_t n;
-    uint32_t v0b, v0d, v0c;
-    __asm__("cpuid" : "=a"(n), "=b"(v0b), "=d"(v0d), "=c"(v0c) : "a"(0));
-    vendor_[0] = v0b;
-    vendor_[1] = v0d;
-    vendor_[2] = v0c;
-    if (n >= 1) {
-      uint32_t f1a;
-      __asm__("cpuid" : "=a"(f1a), "=c"(f1c_), "=d"(f1d_) : "a"(1) : "ebx");
+      x86_cpuid(reg, 1);
+      f1c_ = reg[2];
+      f1d_ = reg[3];
     }
     if (n >= 7) {
-      uint32_t f7a;
-      __asm__("cpuid"
-              : "=a"(f7a), "=b"(f7b_), "=c"(f7c_), "=d"(f7d_)
-              : "a"(7), "c"(0));
+      x86_cpuid(reg, 7);
+      f7b_ = reg[1];
+      f7c_ = reg[2];
+      f7d_ = reg[3];
     }
-#endif
   }
 
 #define FOLLY_DETAIL_CPUID_X(name, r, bit) \
diff --git a/folly/folly/Demangle.cpp b/folly/folly/Demangle.cpp
--- a/folly/folly/Demangle.cpp
+++ b/folly/folly/Demangle.cpp
@@ -20,13 +20,12 @@
 #include <cstring>
 
 #include <folly/CPortability.h>
-#include <folly/CppAttributes.h>
 #include <folly/Utility.h>
 #include <folly/functional/Invoke.h>
 #include <folly/lang/CString.h>
 
 #if __has_include(<cxxabi.h>)
-#include <cxxabi.h>
+#include <cxxabi.h> // @donotremove
 #endif
 
 //  The headers <libiberty.h> (binutils) and <string.h> (glibc) both declare the
@@ -69,7 +68,10 @@
 namespace {
 struct poison {};
 
-[[maybe_unused]] FOLLY_ERASE void rust_demangle_callback(poison);
+FOLLY_PUSH_WARNING
+FOLLY_GCC_DISABLE_WARNING("-Wunused-function")
+[[maybe_unused]] void rust_demangle_callback(poison);
+FOLLY_POP_WARNING
 
 [[maybe_unused]] FOLLY_ERASE int rust_demangle_callback_fallback(
     const char*, int, demangle_callbackref, void*) {
@@ -125,10 +127,16 @@
 
 namespace folly {
 
-bool const demangle_build_has_cxxabi = cxxabi_demangle;
-bool const demangle_build_has_liberty = //
-    to_bool(liberty_cplus_demangle) && //
-    to_bool(liberty_rust_demangle);
+bool demangle_build_has_cxxabi() noexcept {
+  return to_bool(cxxabi_demangle);
+}
+bool demangle_build_has_liberty() noexcept {
+  bool vals[] = {
+      to_bool(liberty_cplus_demangle),
+      to_bool(liberty_rust_demangle),
+  };
+  return std::all_of(std::begin(vals), std::end(vals), folly::identity);
+}
 
 namespace {
 void demangleStringCallback(const char* str, size_t size, void* p) {
@@ -155,7 +163,7 @@
     }
   }
 
-  if (folly::demangle_build_has_liberty) {
+  if (folly::demangle_build_has_liberty()) {
     liberty_demangle_t* funcs[] = {
         liberty_rust_demangle,
         liberty_cplus_demangle,
@@ -223,7 +231,7 @@
     }
   }
 
-  if (folly::demangle_build_has_liberty) {
+  if (folly::demangle_build_has_liberty()) {
     liberty_demangle_t* funcs[] = {
         liberty_rust_demangle,
         liberty_cplus_demangle,
diff --git a/folly/folly/Demangle.h b/folly/folly/Demangle.h
--- a/folly/folly/Demangle.h
+++ b/folly/folly/Demangle.h
@@ -28,8 +28,8 @@
     0;
 #endif
 
-extern bool const demangle_build_has_cxxabi;
-extern bool const demangle_build_has_liberty;
+bool demangle_build_has_cxxabi() noexcept;
+bool demangle_build_has_liberty() noexcept;
 
 /**
  * Return the demangled (prettified) version of a C++ type.
diff --git a/folly/folly/DynamicConverter.h b/folly/folly/DynamicConverter.h
--- a/folly/folly/DynamicConverter.h
+++ b/folly/folly/DynamicConverter.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/json/DynamicConverter.h>
+#include <folly/json/DynamicConverter.h> // @shim
diff --git a/folly/folly/ExceptionWrapper-inl.h b/folly/folly/ExceptionWrapper-inl.h
--- a/folly/folly/ExceptionWrapper-inl.h
+++ b/folly/folly/ExceptionWrapper-inl.h
@@ -73,9 +73,10 @@
 template <
     class Ex,
     class Ex_,
-    FOLLY_REQUIRES_DEF(Conjunction<
-                       exception_wrapper::IsStdException<Ex_>,
-                       exception_wrapper::IsRegularExceptionType<Ex_>>::value)>
+    FOLLY_REQUIRES_DEF(
+        Conjunction<
+            exception_wrapper::IsStdException<Ex_>,
+            exception_wrapper::IsRegularExceptionType<Ex_>>::value)>
 inline exception_wrapper::exception_wrapper(Ex&& ex)
     : ptr_{make_exception_ptr_with(std::in_place, std::forward<Ex>(ex))} {}
 
@@ -131,7 +132,8 @@
   return !!ptr_;
 }
 
-inline std::exception* exception_wrapper::get_exception() noexcept {
+inline std::exception* exception_wrapper::get_mutable_exception()
+    const noexcept {
   return exception_ptr_get_object<std::exception>(ptr_);
 }
 inline std::exception const* exception_wrapper::get_exception() const noexcept {
@@ -139,7 +141,7 @@
 }
 
 template <typename Ex>
-inline Ex* exception_wrapper::get_exception() noexcept {
+inline Ex* exception_wrapper::get_mutable_exception() const noexcept {
   return exception_ptr_get_object_hint<Ex>(ptr_);
 }
 
@@ -209,8 +211,13 @@
 
 template <class This, class Fn, typename Ex>
 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_, tag_t<Ex>) {
-  auto ptr = this_.template get_exception<remove_cvref_t<Ex>>();
-  return ptr && (void(fn_(static_cast<Ex&>(*ptr))), true);
+  if constexpr (std::is_const_v<Ex>) {
+    auto ptr = this_.template get_exception<remove_cvref_t<Ex>>();
+    return ptr && (void(fn_(static_cast<Ex&>(*ptr))), true);
+  } else {
+    auto ptr = this_.template get_mutable_exception<remove_cvref_t<Ex>>();
+    return ptr && (void(fn_(static_cast<Ex&>(*ptr))), true);
+  }
 }
 
 template <class Ex, class This, class Fn>
diff --git a/folly/folly/ExceptionWrapper.h b/folly/folly/ExceptionWrapper.h
--- a/folly/folly/ExceptionWrapper.h
+++ b/folly/folly/ExceptionWrapper.h
@@ -25,6 +25,7 @@
 #include <type_traits>
 #include <typeinfo>
 #include <utility>
+#include <fmt/format.h>
 
 #include <folly/CPortability.h>
 #include <folly/CppAttributes.h>
@@ -204,8 +205,7 @@
 
   //! \return a pointer to the `std::exception` held by `*this`, if it holds
   //!     one; otherwise, returns `nullptr`.
-  std::exception* get_exception() noexcept;
-  //! \overload
+  std::exception* get_mutable_exception() const noexcept;
   std::exception const* get_exception() const noexcept;
 
   //! \returns a pointer to the `Ex` held by `*this`, if it holds an object
@@ -217,10 +217,10 @@
   //!
   //! This is most efficient when `Ex` matches the exact stored type, or when
   //! the type alias `Ex::folly_get_exception_hint_types` has a good hint.
-  template <typename Ex>
-  Ex* get_exception() noexcept;
   //! \overload
   template <typename Ex>
+  Ex* get_mutable_exception() const noexcept;
+  template <typename Ex>
   Ex const* get_exception() const noexcept;
 
   //! \return A `std::exception_ptr` that references the exception held by
@@ -285,7 +285,7 @@
   //! assert( !exception_wrapper{}.with_exception([](int& e){/*...*/}) );
   //! \endcode
   //! \tparam Ex Optionally, the type of the exception that `fn` accepts.
-  //! \tparam Fn The type of a monomophic function object.
+  //! \tparam Fn The type of a monomorphic function object.
   //! \param fn A function object to call with the wrapped exception
   //! \return `true` if and only if `fn` was called.
   //! \note Optionally, you may explicitly specify the type of the exception
@@ -350,8 +350,8 @@
     return get_exception<Ex>();
   }
   template <typename Ex>
-  Ex* get_mutable_exception(get_exception_tag_t) noexcept {
-    return get_exception<Ex>();
+  Ex* get_mutable_exception(get_exception_tag_t) const noexcept {
+    return get_mutable_exception<Ex>();
   }
 };
 
@@ -395,7 +395,23 @@
   auto x = [&] { return void(static_cast<F&&>(fn)()), std::exception_ptr{}; };
   return exception_wrapper{catch_exception(x, current_exception)};
 }
+
+/**
+ * A convenience shorthand for `exception_wrapper(current_exception())`.
+ */
+inline exception_wrapper current_exception_wrapper() noexcept {
+  return exception_wrapper{current_exception()};
+}
+
 } // namespace folly
+
+template <>
+struct fmt::formatter<folly::exception_wrapper> : formatter<folly::fbstring> {
+  template <typename Context>
+  auto format(const folly::exception_wrapper& ew, Context& ctx) const {
+    return formatter<folly::fbstring>::format(ew.what(), ctx);
+  }
+};
 
 #include <folly/ExceptionWrapper-inl.h>
 
diff --git a/folly/folly/Executor.h b/folly/folly/Executor.h
--- a/folly/folly/Executor.h
+++ b/folly/folly/Executor.h
@@ -49,163 +49,162 @@
 
 } // namespace detail
 
-/// An Executor accepts units of work with add(), which should be
-/// threadsafe.
-class Executor {
+class Executor;
+
+/**
+ * `ExecutorKeepAlive` is a safe pointer to an `Executor`.
+ *
+ * For any `Executor` that supports keep-alive functionality, its destructor
+ * will block until all the `ExecutorKeepAlive` objects associated with that
+ * executor are destroyed.  For executors that don't support the keep-alive
+ * functionality, `ExecutorKeepAlive` doesn't provide such protection.
+ *
+ * `ExecutorKeepAlive` should *always* be used instead of `Executor*`.
+ * `ExecutorKeepAlive` can be implicitly constructed from `Executor*`.
+ *
+ * The `getKeepAliveToken()` helper can be used to construct a keep-alive in
+ * templated code if you need to preserve the original executor type.
+ */
+template <typename ExecutorT = Executor>
+class ExecutorKeepAlive : private detail::ExecutorKeepAliveBase {
  public:
-  virtual ~Executor() = default;
+  using KeepAliveFunc = Function<void(ExecutorKeepAlive&&)>;
 
-  /// Enqueue a function to be executed by this executor. This and all
-  /// variants must be threadsafe.
-  virtual void add(Func) = 0;
+  ExecutorKeepAlive() = default;
 
-  /// Enqueue a function with a given priority, where 0 is the medium priority
-  /// This is up to the implementation to enforce
-  virtual void addWithPriority(Func, int8_t priority);
+  ~ExecutorKeepAlive() {
+    static_assert(
+        std::is_standard_layout<ExecutorKeepAlive>::value, "standard-layout");
+    static_assert(sizeof(ExecutorKeepAlive) == sizeof(void*), "pointer size");
+    static_assert(
+        alignof(ExecutorKeepAlive) == alignof(void*), "pointer align");
 
-  virtual uint8_t getNumPriorities() const { return 1; }
+    reset();
+  }
 
-  static constexpr int8_t LO_PRI = SCHAR_MIN;
-  static constexpr int8_t MID_PRI = 0;
-  static constexpr int8_t HI_PRI = SCHAR_MAX;
+  ExecutorKeepAlive(ExecutorKeepAlive&& other) noexcept
+      : storage_(std::exchange(other.storage_, 0)) {}
 
-  /**
-   * Executor::KeepAlive is a safe pointer to an Executor.
-   * For any Executor that supports KeepAlive functionality, Executor's
-   * destructor will block until all the KeepAlive objects associated with that
-   * Executor are destroyed.
-   * For Executors that don't support the KeepAlive functionality, KeepAlive
-   * doesn't provide such protection.
-   *
-   * KeepAlive should *always* be used instead of Executor*. KeepAlive can be
-   * implicitly constructed from Executor*. getKeepAliveToken() helper method
-   * can be used to construct a KeepAlive in templated code if you need to
-   * preserve the original Executor type.
-   */
-  template <typename ExecutorT = Executor>
-  class KeepAlive : private detail::ExecutorKeepAliveBase {
-   public:
-    using KeepAliveFunc = Function<void(KeepAlive&&)>;
+  ExecutorKeepAlive(const ExecutorKeepAlive& other) noexcept;
 
-    KeepAlive() = default;
+  template <
+      typename OtherExecutor,
+      typename = typename std::enable_if<
+          std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
+  /* implicit */ ExecutorKeepAlive(
+      ExecutorKeepAlive<OtherExecutor>&& other) noexcept
+      : ExecutorKeepAlive(other.get(), other.storage_ & kFlagMask) {
+    other.storage_ = 0;
+  }
 
-    ~KeepAlive() {
-      static_assert(
-          std::is_standard_layout<KeepAlive>::value, "standard-layout");
-      static_assert(sizeof(KeepAlive) == sizeof(void*), "pointer size");
-      static_assert(alignof(KeepAlive) == alignof(void*), "pointer align");
+  template <
+      typename OtherExecutor,
+      typename = typename std::enable_if<
+          std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
+  /* implicit */ ExecutorKeepAlive(
+      const ExecutorKeepAlive<OtherExecutor>& other) noexcept;
 
-      reset();
-    }
+  /* implicit */ ExecutorKeepAlive(ExecutorT* executor);
 
-    KeepAlive(KeepAlive&& other) noexcept
-        : storage_(std::exchange(other.storage_, 0)) {}
+  ExecutorKeepAlive& operator=(ExecutorKeepAlive&& other) noexcept {
+    reset();
+    storage_ = std::exchange(other.storage_, 0);
+    return *this;
+  }
 
-    KeepAlive(const KeepAlive& other) noexcept
-        : KeepAlive(getKeepAliveToken(other.get())) {}
+  ExecutorKeepAlive& operator=(ExecutorKeepAlive const& other) {
+    return operator=(folly::copy(other));
+  }
 
-    template <
-        typename OtherExecutor,
-        typename = typename std::enable_if<
-            std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
-    /* implicit */ KeepAlive(KeepAlive<OtherExecutor>&& other) noexcept
-        : KeepAlive(other.get(), other.storage_ & kFlagMask) {
-      other.storage_ = 0;
-    }
+  template <
+      typename OtherExecutor,
+      typename = typename std::enable_if<
+          std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
+  ExecutorKeepAlive& operator=(
+      ExecutorKeepAlive<OtherExecutor>&& other) noexcept {
+    return *this = ExecutorKeepAlive(std::move(other));
+  }
 
-    template <
-        typename OtherExecutor,
-        typename = typename std::enable_if<
-            std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
-    /* implicit */ KeepAlive(const KeepAlive<OtherExecutor>& other) noexcept
-        : KeepAlive(getKeepAliveToken(other.get())) {}
+  template <
+      typename OtherExecutor,
+      typename = typename std::enable_if<
+          std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
+  ExecutorKeepAlive& operator=(const ExecutorKeepAlive<OtherExecutor>& other) {
+    return *this = ExecutorKeepAlive(other);
+  }
 
-    /* implicit */ KeepAlive(ExecutorT* executor) {
-      *this = getKeepAliveToken(executor);
-    }
+  void reset() noexcept;
 
-    KeepAlive& operator=(KeepAlive&& other) noexcept {
-      reset();
-      storage_ = std::exchange(other.storage_, 0);
-      return *this;
-    }
+  explicit operator bool() const { return storage_; }
 
-    KeepAlive& operator=(KeepAlive const& other) {
-      return operator=(folly::copy(other));
-    }
+  ExecutorT* get() const {
+    return reinterpret_cast<ExecutorT*>(storage_ & kExecutorMask);
+  }
 
-    template <
-        typename OtherExecutor,
-        typename = typename std::enable_if<
-            std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
-    KeepAlive& operator=(KeepAlive<OtherExecutor>&& other) noexcept {
-      return *this = KeepAlive(std::move(other));
-    }
+  ExecutorT& operator*() const { return *get(); }
 
-    template <
-        typename OtherExecutor,
-        typename = typename std::enable_if<
-            std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
-    KeepAlive& operator=(const KeepAlive<OtherExecutor>& other) {
-      return *this = KeepAlive(other);
-    }
+  ExecutorT* operator->() const { return get(); }
 
-    void reset() noexcept {
-      if (Executor* executor = get()) {
-        auto const flags = std::exchange(storage_, 0) & kFlagMask;
-        if (!(flags & (kDummyFlag | kAliasFlag))) {
-          executor->keepAliveRelease();
-        }
-      }
-    }
+  ExecutorKeepAlive copy() const;
 
-    explicit operator bool() const { return storage_; }
+  ExecutorKeepAlive get_alias() const {
+    return ExecutorKeepAlive(storage_ | kAliasFlag);
+  }
 
-    ExecutorT* get() const {
-      return reinterpret_cast<ExecutorT*>(storage_ & kExecutorMask);
-    }
+  template <class KAF>
+  void add(KAF&& f) && {
+    static_assert(
+        is_invocable<KAF, ExecutorKeepAlive&&>::value,
+        "Parameter to add must be void(ExecutorKeepAlive&&)>");
+    auto ex = get();
+    ex->add([ka = std::move(*this), f_2 = std::forward<KAF>(f)]() mutable {
+      f_2(std::move(ka));
+    });
+  }
 
-    ExecutorT& operator*() const { return *get(); }
+ private:
+  friend class Executor;
+  template <typename OtherExecutor>
+  friend class ExecutorKeepAlive;
 
-    ExecutorT* operator->() const { return get(); }
+  ExecutorKeepAlive(ExecutorT* executor, uintptr_t flags) noexcept
+      : storage_(reinterpret_cast<uintptr_t>(executor) | flags) {
+    assert(executor);
+    assert(!(reinterpret_cast<uintptr_t>(executor) & ~kExecutorMask));
+    assert(!(flags & kExecutorMask));
+  }
 
-    KeepAlive copy() const {
-      return isKeepAliveDummy(*this) //
-          ? makeKeepAliveDummy(get())
-          : getKeepAliveToken(get());
-    }
+  explicit ExecutorKeepAlive(uintptr_t storage) noexcept : storage_(storage) {}
 
-    KeepAlive get_alias() const { return KeepAlive(storage_ | kAliasFlag); }
+  //  Combined storage for the executor pointer and for all flags.
+  uintptr_t storage_{reinterpret_cast<uintptr_t>(nullptr)};
+};
 
-    template <class KAF>
-    void add(KAF&& f) && {
-      static_assert(
-          is_invocable<KAF, KeepAlive&&>::value,
-          "Parameter to add must be void(KeepAlive&&)>");
-      auto ex = get();
-      ex->add([ka = std::move(*this), f_2 = std::forward<KAF>(f)]() mutable {
-        f_2(std::move(ka));
-      });
-    }
+/// An Executor accepts units of work with add(), which should be
+/// threadsafe.
+class Executor {
+ public:
+  virtual ~Executor() = default;
 
-   private:
-    friend class Executor;
-    template <typename OtherExecutor>
-    friend class KeepAlive;
+  /// Enqueue a function to be executed by this executor. This and all
+  /// variants must be threadsafe.
+  virtual void add(Func) = 0;
 
-    KeepAlive(ExecutorT* executor, uintptr_t flags) noexcept
-        : storage_(reinterpret_cast<uintptr_t>(executor) | flags) {
-      assert(executor);
-      assert(!(reinterpret_cast<uintptr_t>(executor) & ~kExecutorMask));
-      assert(!(flags & kExecutorMask));
-    }
+  /// Enqueue a function with a given priority, where 0 is the medium priority
+  /// This is up to the implementation to enforce
+  virtual void addWithPriority(Func, int8_t priority);
 
-    explicit KeepAlive(uintptr_t storage) noexcept : storage_(storage) {}
+  virtual uint8_t getNumPriorities() const { return 1; }
 
-    //  Combined storage for the executor pointer and for all flags.
-    uintptr_t storage_{reinterpret_cast<uintptr_t>(nullptr)};
-  };
+  static constexpr int8_t LO_PRI = SCHAR_MIN;
+  static constexpr int8_t MID_PRI = 0;
+  static constexpr int8_t HI_PRI = SCHAR_MAX;
 
+  // Compatibility shim.  Cannot be forward-declared, unlike
+  // `ExecutorKeepAlive`.
+  template <typename ExecutorT = Executor>
+  using KeepAlive = ExecutorKeepAlive<ExecutorT>;
+
   template <typename ExecutorT>
   static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) {
     static_assert(
@@ -235,6 +234,9 @@
   }
 
  protected:
+  template <typename>
+  friend class ExecutorKeepAlive;
+
   /**
    * Returns true if the KeepAlive is constructed from an executor that does
    * not support the keep alive ref-counting functionality
@@ -277,6 +279,39 @@
     return KeepAlive<ExecutorT>{executor, KeepAlive<ExecutorT>::kDummyFlag};
   }
 };
+
+template <typename ExecutorT>
+ExecutorKeepAlive<ExecutorT>::ExecutorKeepAlive(
+    const ExecutorKeepAlive<ExecutorT>& other) noexcept
+    : ExecutorKeepAlive(Executor::getKeepAliveToken(other.get())) {}
+
+template <typename ExecutorT>
+template <typename OtherExecutor, typename>
+ExecutorKeepAlive<ExecutorT>::ExecutorKeepAlive(
+    const ExecutorKeepAlive<OtherExecutor>& other) noexcept
+    : ExecutorKeepAlive(Executor::getKeepAliveToken(other.get())) {}
+
+template <typename ExecutorT>
+ExecutorKeepAlive<ExecutorT>::ExecutorKeepAlive(ExecutorT* executor) {
+  *this = Executor::getKeepAliveToken(executor);
+}
+
+template <typename ExecutorT>
+void ExecutorKeepAlive<ExecutorT>::reset() noexcept {
+  if (Executor* executor = get()) {
+    auto const flags = std::exchange(storage_, 0) & kFlagMask;
+    if (!(flags & (kDummyFlag | kAliasFlag))) {
+      executor->keepAliveRelease();
+    }
+  }
+}
+
+template <typename ExecutorT>
+ExecutorKeepAlive<ExecutorT> ExecutorKeepAlive<ExecutorT>::copy() const {
+  return Executor::isKeepAliveDummy(*this) //
+      ? Executor::makeKeepAliveDummy(get())
+      : Executor::getKeepAliveToken(get());
+}
 
 /// Returns a keep-alive token which guarantees that Executor will keep
 /// processing tasks until the token is released (if supported by Executor).
diff --git a/folly/folly/Expected.h b/folly/folly/Expected.h
--- a/folly/folly/Expected.h
+++ b/folly/folly/Expected.h
@@ -65,7 +65,7 @@
  * Expected objects in the error state.
  */
 template <class Error>
-class FOLLY_NODISCARD Unexpected final {
+class [[nodiscard]] Unexpected final {
   template <class E>
   friend class Unexpected;
   template <class V, class E>
@@ -103,10 +103,10 @@
   /**
    * Observers
    */
-  Error& error() & { return error_; }
-  const Error& error() const& { return error_; }
-  Error&& error() && { return std::move(error_); }
-  const Error&& error() const&& { return std::move(error_); }
+  Error& error() & noexcept { return error_; }
+  const Error& error() const& noexcept { return error_; }
+  Error&& error() && noexcept { return std::move(error_); }
+  const Error&& error() const&& noexcept { return std::move(error_); }
 
  private:
   Error error_;
@@ -193,7 +193,7 @@
 class Expected;
 
 template <class Error, class Value>
-FOLLY_NODISCARD constexpr Expected<typename std::decay<Value>::type, Error>
+[[nodiscard]] constexpr Expected<typename std::decay<Value>::type, Error>
 makeExpected(Value&&);
 
 /**
@@ -512,6 +512,7 @@
         this->error().~Error();
         break;
       case Which::eEmpty:
+      default:
         break;
     }
     this->which_ = Which::eEmpty;
@@ -672,11 +673,10 @@
       class... Fns,
       class E = ExpectedErrorType<This>,
       class T = ExpectedHelper>
-  static auto then_(This&& ex, Fn&& fn, Fns&&... fns)
-      -> decltype(T::then_(
-          T::template return_<E>(
-              (std::declval<Fn>()(std::declval<This>().value()), unit)),
-          std::declval<Fns>()...)) {
+  static auto then_(This&& ex, Fn&& fn, Fns&&... fns) -> decltype(T::then_(
+      T::template return_<E>(
+          (std::declval<Fn>()(std::declval<This>().value()), unit)),
+      std::declval<Fns>()...)) {
     if (FOLLY_LIKELY(ex.which_ == expected_detail::Which::eValue)) {
       return T::then_(
           T::template return_<E>(
@@ -745,10 +745,11 @@
     // Note - this basically decays into then_ once the first type (No) is
     // called for the error.
     if (FOLLY_LIKELY(ex.which_ == expected_detail::Which::eValue)) {
-      return T::template return_<E>((T::then_(T::template return_<E>(
-          // Uses the comma operator defined above IFF the lambda
-          // returns non-void.
-          static_cast<decltype(ex)&&>(ex).value()))));
+      return T::template return_<E>((T::then_(
+          T::template return_<E>(
+              // Uses the comma operator defined above IFF the lambda
+              // returns non-void.
+              static_cast<decltype(ex)&&>(ex).value()))));
     }
     return T::then_(
         T::template return_<E>(
@@ -887,7 +888,7 @@
  *     .then([](C c){return D(c);});
  *
  * To avoid the redundant error checking that would happen if a call at the
- * front of the chain returns an error, these call chains can be collaped into
+ * front of the chain returns an error, these call chains can be collapsed into
  * a single call to .then:
  *
  * Expected<D, Error> maybeD = someFunc()
@@ -933,7 +934,7 @@
  * to-Error or vice versa) throws. Trying to access either the contained value
  * or error object causes Expected to throw folly::BadExpectedAccess.
  *
- * Expected models OptionalPointee, so calling 'get_pointer(ex)' will return a
+ * Expected models OptionalPointer, so calling 'get_pointer(ex)' will return a
  * pointer to nullptr if the 'ex' is in the error state, and a pointer to the
  * value otherwise:
  *
@@ -1265,9 +1266,9 @@
    * then
    */
   template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
-  auto then(Fns&&... fns)
-      const& -> decltype(expected_detail::ExpectedHelper::then_(
-                 std::declval<const Base&>(), std::declval<Fns>()...)) {
+  auto
+  then(Fns&&... fns) const& -> decltype(expected_detail::ExpectedHelper::then_(
+      std::declval<const Base&>(), std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
@@ -1276,9 +1277,8 @@
   }
 
   template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
-  auto
-  then(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::then_(
-                           std::declval<Base&>(), std::declval<Fns>()...)) {
+  auto then(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::then_(
+      std::declval<Base&>(), std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
@@ -1287,9 +1287,8 @@
   }
 
   template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
-  auto
-  then(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::then_(
-                            std::declval<Base&&>(), std::declval<Fns>()...)) {
+  auto then(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::then_(
+      std::declval<Base&&>(), std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
@@ -1304,7 +1303,7 @@
   template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
   auto orElse(Fns&&... fns)
       const& -> decltype(expected_detail::ExpectedHelper::orElse_(
-                 std::declval<const Base&>(), std::declval<Fns>()...)) {
+          std::declval<const Base&>(), std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
@@ -1315,7 +1314,7 @@
   template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
   auto
   orElse(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::orElse_(
-                             std::declval<Base&>(), std::declval<Fns>()...)) {
+      std::declval<Base&>(), std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
@@ -1326,7 +1325,7 @@
   template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
   auto
   orElse(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::orElse_(
-                              std::declval<Base&&>(), std::declval<Fns>()...)) {
+      std::declval<Base&&>(), std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
@@ -1344,34 +1343,34 @@
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
-    return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
-        base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+    return Ret(
+        expected_detail::ExpectedHelper::thenOrThrow_(
+            base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
 
   template <class Yes, class No = MakeBadExpectedAccess>
-  auto thenOrThrow(
-      Yes&& yes,
-      No&& no =
-          No{}) & -> decltype(std::declval<Yes>()(std::declval<Value&>())) {
+  auto thenOrThrow(Yes&& yes, No&& no = No{}) & -> decltype(std::declval<Yes>()(
+      std::declval<Value&>())) {
     using Ret = decltype(std::declval<Yes>()(std::declval<Value&>()));
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
-    return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
-        base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+    return Ret(
+        expected_detail::ExpectedHelper::thenOrThrow_(
+            base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
 
   template <class Yes, class No = MakeBadExpectedAccess>
-  auto thenOrThrow(
-      Yes&& yes,
-      No&& no =
-          No{}) && -> decltype(std::declval<Yes>()(std::declval<Value&&>())) {
+  auto
+  thenOrThrow(Yes&& yes, No&& no = No{}) && -> decltype(std::declval<Yes>()(
+      std::declval<Value&&>())) {
     using Ret = decltype(std::declval<Yes>()(std::declval<Value&&>()));
     if (this->uninitializedByException()) {
       throw_exception<BadExpectedAccess<void>>();
     }
-    return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
-        std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+    return Ret(
+        expected_detail::ExpectedHelper::thenOrThrow_(
+            std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
 
   /**
@@ -1551,7 +1550,7 @@
  * }
  */
 template <class Error, class Value>
-FOLLY_NODISCARD constexpr Expected<typename std::decay<Value>::type, Error>
+[[nodiscard]] constexpr Expected<typename std::decay<Value>::type, Error>
 makeExpected(Value&& val) {
   return Expected<typename std::decay<Value>::type, Error>{
       std::in_place, static_cast<Value&&>(val)};
@@ -1590,6 +1589,8 @@
 
 // Enable the use of folly::Expected with `co_await`
 // Inspired by https://github.com/toby-allsopp/coroutine_monad
+//
+// NOTE: Please port applicable improvements to `folly/result/coro.h`.
 #if FOLLY_HAS_COROUTINES
 #include <folly/coro/Coroutine.h>
 
@@ -1632,12 +1633,8 @@
   PromiseBase(PromiseBase const&) = delete;
   void operator=(PromiseBase const&) = delete;
 
-  [[nodiscard]] coro::suspend_never initial_suspend() const noexcept {
-    return {};
-  }
-  [[nodiscard]] coro::suspend_never final_suspend() const noexcept {
-    return {};
-  }
+  coro::suspend_never initial_suspend() const noexcept { return {}; }
+  coro::suspend_never final_suspend() const noexcept { return {}; }
   [[noreturn]] void unhandled_exception() {
     // Technically, throwing from unhandled_exception is underspecified:
     // https://github.com/GorNishanov/CoroutineWording/issues/17
@@ -1684,8 +1681,9 @@
   void await_resume() { compiler_may_unsafely_assume_unreachable(); }
 
   template <typename U>
-  FOLLY_ALWAYS_INLINE void await_suspend(
-      coro::coroutine_handle<Promise<U, Error>> h) {
+  FOLLY_ALWAYS_INLINE void
+  await_suspend(coro::coroutine_handle<Promise<U, Error>> h) noexcept(
+      IsNothrowMovable<Error>::value) {
     auto& v = *h.promise().value_;
     ExpectedHelper::assume_empty(v);
     v = std::move(o_);
@@ -1704,8 +1702,9 @@
 
   // Explicitly only allow suspension into a Promise
   template <typename U>
-  FOLLY_ALWAYS_INLINE void await_suspend(
-      coro::coroutine_handle<Promise<U, Error>> h) {
+  FOLLY_ALWAYS_INLINE void
+  await_suspend(coro::coroutine_handle<Promise<U, Error>> h) noexcept(
+      IsNothrowMovable<Error>::value) {
     auto& v = *h.promise().value_;
     ExpectedHelper::assume_empty(v);
     v = makeUnexpected(std::move(o_.error()));
diff --git a/folly/folly/FBString.h b/folly/folly/FBString.h
--- a/folly/folly/FBString.h
+++ b/folly/folly/FBString.h
@@ -205,7 +205,7 @@
 
 /**
  * This is the core of the string. The code should work on 32- and
- * 64-bit and both big- and little-endianan architectures with any
+ * 64-bit and both big- and little-endian architectures with any
  * Char size.
  *
  * The storage is selected as follows (assuming we store one-byte
@@ -344,8 +344,9 @@
         return ml_.data_;
       case Category::isLarge:
         return mutableDataLarge();
+      default:
+        folly::assume_unreachable();
     }
-    folly::assume_unreachable();
   }
 
   const Char* c_str() const {
@@ -437,6 +438,20 @@
     return category() == Category::isLarge && RefCounted::refs(ml_.data_) > 1;
   }
 
+  // Returns the reference count for Large (RefCounted) strings, or 1 for
+  // Small/Medium strings which are not reference counted.
+  size_t useCount() const {
+    if (category() == Category::isLarge) {
+      return RefCounted::refs(ml_.data_);
+    }
+    return 1;
+  }
+
+  // Returns true if the string is using RefCounted (Large) storage mode.
+  // Large strings have a RefCounted header that adds sizeof(size_t) bytes
+  // to the allocation, which is important for accurate memory accounting.
+  bool isCounted() const { return category() == Category::isLarge; }
+
  private:
   Char* c_str() {
     Char* ptr = ml_.data_;
@@ -927,6 +942,8 @@
   size_t size() const { return backend_.size(); }
   size_t capacity() const { return backend_.capacity(); }
   bool isShared() const { return false; }
+  size_t useCount() const { return 1; }
+  bool isCounted() const { return false; }
   void reserve(size_t minCapacity) { backend_.reserve(minCapacity); }
 
  private:
@@ -1220,6 +1237,17 @@
   void resize(size_type n, value_type c = value_type());
 
   size_type capacity() const { return store_.capacity(); }
+
+  // Returns the reference count for this string's underlying data.
+  // Returns 1 for Small (SSO) or Medium strings which are not reference
+  // counted. For Large strings, returns the actual reference count.
+  // Useful for memory accounting with fair-share division.
+  size_t use_count() const { return store_.useCount(); }
+
+  // Returns true if the string uses RefCounted (Large) storage mode.
+  // Large strings have a RefCounted header (sizeof(size_t) bytes) before
+  // the data, which must be accounted for in memory calculations.
+  bool is_counted() const { return store_.isCounted(); }
 
   void reserve(size_type res_arg = 0) {
     enforce<std::length_error>(res_arg <= max_size(), "");
diff --git a/folly/folly/FBVector.h b/folly/folly/FBVector.h
--- a/folly/folly/FBVector.h
+++ b/folly/folly/FBVector.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/container/FBVector.h>
+#include <folly/container/FBVector.h> // @shim
diff --git a/folly/folly/File.cpp b/folly/folly/File.cpp
--- a/folly/folly/File.cpp
+++ b/folly/folly/File.cpp
@@ -38,11 +38,12 @@
 File::File(const char* name, int flags, mode_t mode)
     : fd_(fileops::open(name, flags, mode)), ownsFd_(false) {
   if (fd_ == -1) {
-    throwSystemError(fmt::format(
-        FOLLY_FMT_COMPILE("open(\"{}\", {:#o}, 0{:#o}) failed"),
-        name,
-        flags,
-        mode));
+    throwSystemError(
+        fmt::format(
+            FOLLY_FMT_COMPILE("open(\"{}\", {:#o}, 0{:#o}) failed"),
+            name,
+            flags,
+            mode));
   }
   ownsFd_ = true;
 }
diff --git a/folly/folly/FileUtil.cpp b/folly/folly/FileUtil.cpp
--- a/folly/folly/FileUtil.cpp
+++ b/folly/folly/FileUtil.cpp
@@ -53,7 +53,7 @@
   // https://github.com/llvm/llvm-project/commit/0a0e411204a2baa520fd73a8d69b664f98b428ba
   //
   auto openWrapper = [&] { return fileops::open(name, flags, mode); };
-  return int(wrapNoInt(openWrapper));
+  return wrapNoInt(openWrapper);
 }
 
 static int filterCloseReturn(int r) {
@@ -81,41 +81,41 @@
 }
 
 int fsyncNoInt(int fd) {
-  return int(wrapNoInt(fsync, fd));
+  return wrapNoInt(fsync, fd);
 }
 
 int dupNoInt(int fd) {
-  return int(wrapNoInt(dup, fd));
+  return wrapNoInt(dup, fd);
 }
 
 int dup2NoInt(int oldFd, int newFd) {
-  return int(wrapNoInt(dup2, oldFd, newFd));
+  return wrapNoInt(dup2, oldFd, newFd);
 }
 
 int fdatasyncNoInt(int fd) {
 #if defined(__APPLE__)
-  return int(wrapNoInt(fcntl, fd, F_FULLFSYNC));
+  return wrapNoInt(fcntl, fd, F_FULLFSYNC);
 #elif defined(__FreeBSD__) || defined(_MSC_VER)
-  return int(wrapNoInt(fsync, fd));
+  return wrapNoInt(fsync, fd);
 #else
-  return int(wrapNoInt(fdatasync, fd));
+  return wrapNoInt(fdatasync, fd);
 #endif
 }
 
 int ftruncateNoInt(int fd, off_t len) {
-  return int(wrapNoInt(ftruncate, fd, len));
+  return wrapNoInt(ftruncate, fd, len);
 }
 
 int truncateNoInt(const char* path, off_t len) {
-  return int(wrapNoInt(truncate, path, len));
+  return wrapNoInt(truncate, path, len);
 }
 
 int flockNoInt(int fd, int operation) {
-  return int(wrapNoInt(flock, fd, operation));
+  return wrapNoInt(flock, fd, operation);
 }
 
 int shutdownNoInt(NetworkSocket fd, int how) {
-  return int(wrapNoInt(netops::shutdown, fd, how));
+  return wrapNoInt(netops::shutdown, fd, how);
 }
 
 ssize_t readNoInt(int fd, void* buf, size_t count) {
diff --git a/folly/folly/FileUtil.h b/folly/folly/FileUtil.h
--- a/folly/folly/FileUtil.h
+++ b/folly/folly/FileUtil.h
@@ -83,11 +83,10 @@
  * readv and preadv.  The contents of iov after these functions return
  * is unspecified.
  */
-FOLLY_NODISCARD ssize_t readFull(int fd, void* buf, size_t count);
-FOLLY_NODISCARD ssize_t
-preadFull(int fd, void* buf, size_t count, off_t offset);
-FOLLY_NODISCARD ssize_t readvFull(int fd, iovec* iov, int count);
-FOLLY_NODISCARD ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset);
+[[nodiscard]] ssize_t readFull(int fd, void* buf, size_t count);
+[[nodiscard]] ssize_t preadFull(int fd, void* buf, size_t count, off_t offset);
+[[nodiscard]] ssize_t readvFull(int fd, iovec* iov, int count);
+[[nodiscard]] ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset);
 
 /**
  * Similar to readFull and preadFull above, wrappers around write() and
@@ -147,8 +146,10 @@
   // should attempt to read stuff. If not zero, we'll attempt to read
   // one extra byte.
   constexpr size_t initialAlloc = 1024 * 4;
-  out.resize(std::min(
-      buf.st_size > 0 ? (size_t(buf.st_size) + 1) : initialAlloc, num_bytes));
+  out.resize(
+      std::min(
+          buf.st_size > 0 ? (size_t(buf.st_size) + 1) : initialAlloc,
+          num_bytes));
 
   while (soFar < out.size()) {
     const auto actual = readFull(fd, &out[soFar], out.size() - soFar);
diff --git a/folly/folly/Fingerprint.cpp b/folly/folly/Fingerprint.cpp
--- a/folly/folly/Fingerprint.cpp
+++ b/folly/folly/Fingerprint.cpp
@@ -17,7 +17,6 @@
 #include <folly/Fingerprint.h>
 
 #include <folly/Portability.h>
-#include <folly/Utility.h>
 #include <folly/detail/FingerprintPolynomial.h>
 
 #include <utility>
diff --git a/folly/folly/FollyMemcpy.cpp b/folly/folly/FollyMemcpy.cpp
--- a/folly/folly/FollyMemcpy.cpp
+++ b/folly/folly/FollyMemcpy.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <cstring>
+#include <cstring> // @donotremove
 
 #if !defined(__AVX2__) && !(defined(__linux__) && defined(__aarch64__))
 namespace folly {
diff --git a/folly/folly/FollyMemset.cpp b/folly/folly/FollyMemset.cpp
--- a/folly/folly/FollyMemset.cpp
+++ b/folly/folly/FollyMemset.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <cstring>
+#include <cstring> // @donotremove
 
 #if !defined(__AVX2__) && !(defined(__linux__) && defined(__aarch64__))
 
diff --git a/folly/folly/Format.cpp b/folly/folly/Format.cpp
--- a/folly/folly/Format.cpp
+++ b/folly/folly/Format.cpp
@@ -19,11 +19,10 @@
 #include <cassert>
 
 #include <folly/ConstexprMath.h>
-#include <folly/CppAttributes.h>
 #include <folly/Portability.h>
 #include <folly/container/Array.h>
 
-#include <double-conversion/double-conversion.h>
+#include <double-conversion/double-conversion.h> // @donotremove
 
 namespace folly {
 namespace detail {
diff --git a/folly/folly/Format.h b/folly/folly/Format.h
--- a/folly/folly/Format.h
+++ b/folly/folly/Format.h
@@ -57,8 +57,6 @@
 class Formatter;
 template <class... Args>
 Formatter<false, Args...> format(StringPiece fmt, Args&&... args);
-template <class C>
-std::string svformat(StringPiece fmt, C&& container) = delete;
 template <class T, class Enable = void>
 class FormatValue;
 
@@ -158,7 +156,7 @@
   /**
    * Metadata to identify generated children of BaseFormatter
    */
-  typedef detail::FormatterTag IsFormatter;
+  using IsFormatter = detail::FormatterTag;
 
  private:
   template <typename T, typename D = typename std::decay<T>::type>
@@ -249,8 +247,6 @@
       Str* out, StringPiece fmt, A&&... args);
   template <class... A>
   friend std::string sformat(StringPiece fmt, A&&... arg);
-  template <class C>
-  friend std::string svformat(StringPiece fmt, C&& container);
 };
 
 namespace detail {
@@ -287,8 +283,7 @@
 [[deprecated(
     "Use fmt::format instead of folly::format for better performance, build "
     "times and compatibility with std::format")]] //
-Formatter<false, Args...>
-format(StringPiece fmt, Args&&... args) {
+Formatter<false, Args...> format(StringPiece fmt, Args&&... args) {
   return Formatter<false, Args...>(fmt, static_cast<Args&&>(args)...);
 }
 
diff --git a/folly/folly/FormatTraits.h b/folly/folly/FormatTraits.h
--- a/folly/folly/FormatTraits.h
+++ b/folly/folly/FormatTraits.h
@@ -24,7 +24,7 @@
 
 // Shortcut, so we don't have to use enable_if everywhere
 struct FormatTraitsBase {
-  typedef void enabled;
+  using enabled = void;
 };
 
 // Traits that define enabled, value_type, and at() for anything
@@ -36,8 +36,8 @@
 // Base class for sequences (vectors, deques)
 template <class C>
 struct IndexableTraitsSeq : public FormatTraitsBase {
-  typedef C container_type;
-  typedef typename C::value_type value_type;
+  using container_type = C;
+  using value_type = typename C::value_type;
 
   static const value_type& at(const C& c, int idx) { return c.at(idx); }
 
@@ -49,7 +49,7 @@
 // Base class for associative types (maps)
 template <class C>
 struct IndexableTraitsAssoc : public FormatTraitsBase {
-  typedef typename C::value_type::second_type value_type;
+  using value_type = typename C::value_type::second_type;
 
   static const value_type& at(const C& c, int idx) {
     return c.at(static_cast<typename C::key_type>(idx));
diff --git a/folly/folly/Function.h b/folly/folly/Function.h
--- a/folly/folly/Function.h
+++ b/folly/folly/Function.h
@@ -558,8 +558,9 @@
   static std::size_t exec(Op o, Data* src, Data* dst) noexcept {
     switch (o) {
       case Op::MOVE:
-        ::new (static_cast<void*>(&dst->tiny)) Fun(static_cast<Fun&&>(
-            *static_cast<Fun*>(static_cast<void*>(&src->tiny))));
+        ::new (static_cast<void*>(&dst->tiny))
+            Fun(static_cast<Fun&&>(
+                *static_cast<Fun*>(static_cast<void*>(&src->tiny))));
         [[fallthrough]];
       case Op::NUKE:
         static_cast<Fun*>(static_cast<void*>(&src->tiny))->~Fun();
@@ -616,8 +617,8 @@
     InSituSize && InSituAlign && InSituNoexcept,
     std::is_trivially_copyable_v<Fun>>;
 
-// This cannot be done inseide `Function` class, because the word
-// `Function` there refers to the instantion and not the template.
+// This cannot be done inside `Function` class, because the word
+// `Function` there refers to the instantiation and not the template.
 template <typename T>
 constexpr bool is_instantiation_of_folly_function_v =
     is_instantiation_of_v<Function, T>;
@@ -788,7 +789,7 @@
       // Prevent this overload from being selected when `ptr` is not a
       // compatible member function pointer.
       typename = decltype(Function(std::mem_fn((Member Class::*)0)))>
-  /* implicit */ Function(Member Class::*ptr) noexcept {
+  /* implicit */ Function(Member Class::* ptr) noexcept {
     if (ptr) {
       *this = std::mem_fn(ptr);
     }
@@ -876,7 +877,7 @@
    * operator is equivalent to `*this = std::mem_fn(ptr)`.
    */
   template <typename Member, typename Class>
-  auto operator=(Member Class::*ptr) noexcept
+  auto operator=(Member Class::* ptr) noexcept
       // Prevent this overload from being selected when `ptr` is not a
       // compatible member function pointer.
       -> decltype(operator=(std::mem_fn(ptr))) {
diff --git a/folly/folly/GroupVarint.h b/folly/folly/GroupVarint.h
--- a/folly/folly/GroupVarint.h
+++ b/folly/folly/GroupVarint.h
@@ -442,8 +442,8 @@
   static const uint64_t kMask[];
 };
 
-typedef GroupVarint<uint32_t> GroupVarint32;
-typedef GroupVarint<uint64_t> GroupVarint64;
+using GroupVarint32 = GroupVarint<uint32_t>;
+using GroupVarint64 = GroupVarint<uint64_t>;
 
 /**
  * Simplify use of GroupVarint* for the case where data is available one
@@ -456,8 +456,8 @@
 template <class T, class Output>
 class GroupVarintEncoder {
  public:
-  typedef GroupVarint<T> Base;
-  typedef T type;
+  using Base = GroupVarint<T>;
+  using type = T;
 
   explicit GroupVarintEncoder(Output out) : out_(out), count_(0) {}
 
@@ -521,8 +521,8 @@
 template <typename T>
 class GroupVarintDecoder {
  public:
-  typedef GroupVarint<T> Base;
-  typedef T type;
+  using Base = GroupVarint<T>;
+  using type = T;
 
   GroupVarintDecoder() = default;
 
@@ -622,8 +622,8 @@
   size_t remaining_;
 };
 
-typedef GroupVarintDecoder<uint32_t> GroupVarint32Decoder;
-typedef GroupVarintDecoder<uint64_t> GroupVarint64Decoder;
+using GroupVarint32Decoder = GroupVarintDecoder<uint32_t>;
+using GroupVarint64Decoder = GroupVarintDecoder<uint64_t>;
 
 } // namespace folly
 
diff --git a/folly/folly/Hash.h b/folly/folly/Hash.h
--- a/folly/folly/Hash.h
+++ b/folly/folly/Hash.h
@@ -17,4 +17,4 @@
 #pragma once
 
 //  shims:
-#include <folly/hash/Hash.h>
+#include <folly/hash/Hash.h> // @shim
diff --git a/folly/folly/IPAddress.cpp b/folly/folly/IPAddress.cpp
--- a/folly/folly/IPAddress.cpp
+++ b/folly/folly/IPAddress.cpp
@@ -97,9 +97,10 @@
   }
 
   if (ret.error() == CIDRNetworkError::INVALID_IP_SLASH_CIDR) {
-    throw IPAddressFormatException(fmt::format(
-        "Invalid ipSlashCidr specified. Expected IP/CIDR format, got '{}'",
-        ipSlashCidr));
+    throw IPAddressFormatException(
+        fmt::format(
+            "Invalid ipSlashCidr specified. Expected IP/CIDR format, got '{}'",
+            ipSlashCidr));
   }
 
   // Handler the remaining error cases. We re-parse the ip/mask pair
@@ -120,10 +121,11 @@
       auto cidr = static_cast<uint8_t>(
           (defaultCidr > -1) ? defaultCidr : (subnet.isV4() ? 32 : 128));
 
-      throw IPAddressFormatException(fmt::format(
-          "CIDR value '{}' is > network bit count '{}'",
-          vec.size() == 2 ? vec.at(1) : to<string>(cidr),
-          subnet.bitCount()));
+      throw IPAddressFormatException(
+          fmt::format(
+              "CIDR value '{}' is > network bit count '{}'",
+              vec.size() == 2 ? vec.at(1) : to<string>(cidr),
+              subnet.bitCount()));
     }
     case CIDRNetworkError::INVALID_DEFAULT_CIDR:
     case CIDRNetworkError::INVALID_IP_SLASH_CIDR:
@@ -376,10 +378,11 @@
 uint8_t IPAddress::getNthMSByte(size_t byteIndex) const {
   const auto highestIndex = byteCount() - 1;
   if (byteIndex > highestIndex) {
-    throw std::invalid_argument(fmt::format(
-        "Byte index must be <= {} for addresses of type: {}",
-        highestIndex,
-        detail::familyNameStr(family())));
+    throw std::invalid_argument(
+        fmt::format(
+            "Byte index must be <= {} for addresses of type: {}",
+            highestIndex,
+            detail::familyNameStr(family())));
   }
   if (isV4()) {
     return asV4().bytes()[byteIndex];
@@ -452,11 +455,12 @@
 CIDRNetwork IPAddress::longestCommonPrefix(
     const CIDRNetwork& one, const CIDRNetwork& two) {
   if (one.first.family() != two.first.family()) {
-    throw std::invalid_argument(fmt::format(
-        "Can't compute longest common prefix between addresses of different"
-        "families. Passed: {} and {}",
-        detail::familyNameStr(one.first.family()),
-        detail::familyNameStr(two.first.family())));
+    throw std::invalid_argument(
+        fmt::format(
+            "Can't compute longest common prefix between addresses of different"
+            "families. Passed: {} and {}",
+            detail::familyNameStr(one.first.family()),
+            detail::familyNameStr(two.first.family())));
   }
   if (one.first.isV4()) {
     auto prefix = IPAddressV4::longestCommonPrefix(
diff --git a/folly/folly/IPAddressV4.cpp b/folly/folly/IPAddressV4.cpp
--- a/folly/folly/IPAddressV4.cpp
+++ b/folly/folly/IPAddressV4.cpp
@@ -111,9 +111,10 @@
 IPAddressV4 IPAddressV4::fromBinary(ByteRange bytes) {
   auto maybeIp = tryFromBinary(bytes);
   if (maybeIp.hasError()) {
-    throw IPAddressFormatException(to<std::string>(
-        "Invalid IPv4 binary data: length must be 4 bytes, got ",
-        bytes.size()));
+    throw IPAddressFormatException(
+        to<std::string>(
+            "Invalid IPv4 binary data: length must be 4 bytes, got ",
+            bytes.size()));
   }
   return maybeIp.value();
 }
@@ -146,7 +147,7 @@
         fmt::format("input does not end with '.in-addr.arpa': '{}'", arpaname));
   }
   std::vector<StringPiece> pieces;
-  split(".", piece, pieces);
+  split('.', piece, pieces);
   if (pieces.size() != 4) {
     throw IPAddressFormatException(fmt::format("Invalid input. Got {}", piece));
   }
@@ -277,10 +278,11 @@
 uint8_t IPAddressV4::getNthMSByte(size_t byteIndex) const {
   const auto highestIndex = byteCount() - 1;
   if (byteIndex > highestIndex) {
-    throw std::invalid_argument(fmt::format(
-        "Byte index must be <= {} for addresses of type: {}",
-        highestIndex,
-        detail::familyNameStr(AF_INET)));
+    throw std::invalid_argument(
+        fmt::format(
+            "Byte index must be <= {} for addresses of type: {}",
+            highestIndex,
+            detail::familyNameStr(AF_INET)));
   }
   return bytes()[byteIndex];
 }
diff --git a/folly/folly/IPAddressV4.h b/folly/folly/IPAddressV4.h
--- a/folly/folly/IPAddressV4.h
+++ b/folly/folly/IPAddressV4.h
@@ -46,12 +46,12 @@
 /**
  * Pair of IPAddressV4, netmask
  */
-typedef std::pair<IPAddressV4, uint8_t> CIDRNetworkV4;
+using CIDRNetworkV4 = std::pair<IPAddressV4, uint8_t>;
 
 /**
  * Specialization of `std::array` for IPv4 addresses
  */
-typedef std::array<uint8_t, 4> ByteArray4;
+using ByteArray4 = std::array<uint8_t, 4>;
 
 class IPAddressV4 {
  public:
diff --git a/folly/folly/IPAddressV6.cpp b/folly/folly/IPAddressV6.cpp
--- a/folly/folly/IPAddressV6.cpp
+++ b/folly/folly/IPAddressV6.cpp
@@ -177,9 +177,10 @@
 IPAddressV6 IPAddressV6::fromBinary(ByteRange bytes) {
   auto maybeIp = tryFromBinary(bytes);
   if (maybeIp.hasError()) {
-    throw IPAddressFormatException(to<std::string>(
-        "Invalid IPv6 binary data: length must be 16 bytes, got ",
-        bytes.size()));
+    throw IPAddressFormatException(
+        to<std::string>(
+            "Invalid IPv6 binary data: length must be 16 bytes, got ",
+            bytes.size()));
   }
   return maybeIp.value();
 }
@@ -208,11 +209,12 @@
 IPAddressV6 IPAddressV6::fromInverseArpaName(const std::string& arpaname) {
   auto piece = StringPiece(arpaname);
   if (!piece.removeSuffix(".ip6.arpa")) {
-    throw IPAddressFormatException(fmt::format(
-        "Invalid input. Should end with 'ip6.arpa'. Got '{}'", arpaname));
+    throw IPAddressFormatException(
+        fmt::format(
+            "Invalid input. Should end with 'ip6.arpa'. Got '{}'", arpaname));
   }
   std::vector<StringPiece> pieces;
-  split(".", piece, pieces);
+  split('.', piece, pieces);
   if (pieces.size() != 32) {
     throw IPAddressFormatException(
         fmt::format("Invalid input. Got '{}'", piece));
@@ -446,10 +448,11 @@
   char buffer[INET6_ADDRSTRLEN + IFNAMSIZ + 1];
 
   if (!inet_ntop(AF_INET6, toAddr().s6_addr, buffer, INET6_ADDRSTRLEN)) {
-    throw IPAddressFormatException(fmt::format(
-        "Invalid address with hex '{}' with error {}",
-        detail::Bytes::toHex(bytes(), 16),
-        errnoStr(errno)));
+    throw IPAddressFormatException(
+        fmt::format(
+            "Invalid address with hex '{}' with error {}",
+            detail::Bytes::toHex(bytes(), 16),
+            errnoStr(errno)));
   }
 
   auto scopeId = getScopeId();
@@ -496,10 +499,11 @@
 uint8_t IPAddressV6::getNthMSByte(size_t byteIndex) const {
   const auto highestIndex = byteCount() - 1;
   if (byteIndex > highestIndex) {
-    throw std::invalid_argument(fmt::format(
-        "Byte index must be <= {} for addresses of type: {}",
-        highestIndex,
-        detail::familyNameStr(AF_INET6)));
+    throw std::invalid_argument(
+        fmt::format(
+            "Byte index must be <= {} for addresses of type: {}",
+            highestIndex,
+            detail::familyNameStr(AF_INET6)));
   }
   return bytes()[byteIndex];
 }
diff --git a/folly/folly/IPAddressV6.h b/folly/folly/IPAddressV6.h
--- a/folly/folly/IPAddressV6.h
+++ b/folly/folly/IPAddressV6.h
@@ -51,12 +51,12 @@
 /**
  * Pair of IPAddressV6, netmask
  */
-typedef std::pair<IPAddressV6, uint8_t> CIDRNetworkV6;
+using CIDRNetworkV6 = std::pair<IPAddressV6, uint8_t>;
 
 /**
  * Specialization for `std::array` for IPv6 addresses
  */
-typedef std::array<uint8_t, 16> ByteArray16;
+using ByteArray16 = std::array<uint8_t, 16>;
 
 class IPAddressV6 {
  public:
@@ -81,7 +81,7 @@
   /**
    * Alias std::runtime_error, to be thrown when a type assertion fails
    */
-  typedef std::runtime_error TypeError;
+  using TypeError = std::runtime_error;
 
   /**
    * The binary prefix for Teredo networks
diff --git a/folly/folly/Indestructible.h b/folly/folly/Indestructible.h
--- a/folly/folly/Indestructible.h
+++ b/folly/folly/Indestructible.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <cassert>
-#include <new>
 #include <type_traits>
 #include <utility>
 
diff --git a/folly/folly/IndexedMemPool.h b/folly/folly/IndexedMemPool.h
--- a/folly/folly/IndexedMemPool.h
+++ b/folly/folly/IndexedMemPool.h
@@ -157,10 +157,10 @@
     template <typename> class Atom = std::atomic,
     typename Traits = IndexedMemPoolTraits<T>>
 struct IndexedMemPool {
-  typedef T value_type;
+  using value_type = T;
 
-  typedef std::unique_ptr<T, detail::IndexedMemPoolRecycler<IndexedMemPool>>
-      UniquePtr;
+  using UniquePtr =
+      std::unique_ptr<T, detail::IndexedMemPoolRecycler<IndexedMemPool>>;
 
   IndexedMemPool(const IndexedMemPool&) = delete;
   IndexedMemPool& operator=(const IndexedMemPool&) = delete;
@@ -181,9 +181,10 @@
   static constexpr uint32_t maxIndexForCapacity(uint32_t capacity) {
     // index of std::numeric_limits<uint32_t>::max() is reserved for isAllocated
     // tracking
-    return uint32_t(std::min(
-        uint64_t(capacity) + (NumLocalLists - 1) * LocalListLimit,
-        uint64_t(std::numeric_limits<uint32_t>::max() - 1)));
+    return uint32_t(
+        std::min(
+            uint64_t(capacity) + (NumLocalLists - 1) * LocalListLimit,
+            uint64_t(std::numeric_limits<uint32_t>::max() - 1)));
   }
 
   static constexpr uint32_t capacityForMaxIndex(uint32_t maxIndex) {
diff --git a/folly/folly/IntrusiveList.h b/folly/folly/IntrusiveList.h
--- a/folly/folly/IntrusiveList.h
+++ b/folly/folly/IntrusiveList.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/container/IntrusiveList.h>
+#include <folly/container/IntrusiveList.h> // @shim
diff --git a/folly/folly/Lazy.h b/folly/folly/Lazy.h
--- a/folly/folly/Lazy.h
+++ b/folly/folly/Lazy.h
@@ -89,7 +89,7 @@
 
 template <class Func>
 struct Lazy {
-  typedef invoke_result_t<Func> result_type;
+  using result_type = invoke_result_t<Func>;
 
   static_assert(
       !std::is_const<Func>::value, "Func should not be a const-qualified type");
diff --git a/folly/folly/MPMCPipeline.h b/folly/folly/MPMCPipeline.h
--- a/folly/folly/MPMCPipeline.h
+++ b/folly/folly/MPMCPipeline.h
@@ -246,8 +246,9 @@
   /**
    * Pop an element from (the final stage of) the pipeline. Blocking.
    */
-  void blockingRead(typename std::tuple_element<sizeof...(Stages), StageTuple>::
-                        type::value_type& elem) {
+  void blockingRead(
+      typename std::tuple_element<sizeof...(Stages), StageTuple>::type::
+          value_type& elem) {
     std::get<sizeof...(Stages)>(stages_).blockingRead(elem);
   }
 
@@ -255,8 +256,9 @@
    * Try to pop an element from (the final stage of) the pipeline.
    * Non-blocking.
    */
-  bool read(typename std::tuple_element<sizeof...(Stages), StageTuple>::type::
-                value_type& elem) {
+  bool read(
+      typename std::tuple_element<sizeof...(Stages), StageTuple>::type::
+          value_type& elem) {
     return std::get<sizeof...(Stages)>(stages_).read(elem);
   }
 
diff --git a/folly/folly/MPMCQueue.h b/folly/folly/MPMCQueue.h
--- a/folly/folly/MPMCQueue.h
+++ b/folly/folly/MPMCQueue.h
@@ -23,9 +23,11 @@
 #include <limits>
 #include <type_traits>
 
+#include <folly/Portability.h>
 #include <folly/Traits.h>
 #include <folly/concurrency/CacheLocality.h>
 #include <folly/detail/TurnSequencer.h>
+#include <folly/lang/Exception.h>
 #include <folly/portability/Unistd.h>
 
 namespace folly {
@@ -85,11 +87,7 @@
 /// enforced at compile time using type traits, which requires that T be
 /// adorned with accurate noexcept information.  If your type does not
 /// use noexcept, you will have to wrap it in something that provides
-/// the guarantee.  We provide an alternate safe implementation for types
-/// that don't use noexcept but that are marked folly::IsRelocatable
-/// and std::is_nothrow_constructible, which is common for folly types.
-/// In particular, if you can declare FOLLY_ASSUME_FBVECTOR_COMPATIBLE
-/// then your type can be put in MPMCQueue.
+/// the guarantee.
 ///
 /// If you have a pool of N queue consumers that you want to shut down
 /// after the queue has drained, one way is to enqueue N sentinel values
@@ -106,6 +104,9 @@
     : public detail::MPMCQueueBase<MPMCQueue<T, Atom, Dynamic, Allocator>>,
       std::allocator_traits<Allocator>::template rebind_alloc<
           detail::SingleElementQueue<T, Atom>> {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+  static_assert(std::is_nothrow_destructible_v<T>);
+
   friend class detail::MPMCPipelineStageImpl<T>;
   using Base = detail::MPMCQueueBase<MPMCQueue<T, Atom, Dynamic, Allocator>>;
   using Slot = detail::SingleElementQueue<T, Atom>;
@@ -682,15 +683,13 @@
 template <
     template <
         typename T,
-        template <typename>
-        class Atom,
+        template <typename> class Atom,
         bool Dynamic,
         class Allocator>
 
     class Derived,
     typename T,
-    template <typename>
-    class Atom,
+    template <typename> class Atom,
     bool Dynamic,
     class Allocator>
 class MPMCQueueBase<Derived<T, Atom, Dynamic, Allocator>> {
@@ -700,13 +699,11 @@
   // template
   using DerivedType = Derived<T, Atom, Dynamic, Allocator>;
 
-  static_assert(
-      std::is_nothrow_constructible<T, T&&>::value ||
-          folly::IsRelocatable<T>::value,
-      "T must be relocatable or have a noexcept move constructor");
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+  static_assert(std::is_nothrow_destructible_v<T>);
 
  public:
-  typedef T value_type;
+  using value_type = T;
 
   using Slot = detail::SingleElementQueue<T, Atom>;
 
@@ -719,11 +716,14 @@
         pushSpinCutoff_(0),
         popSpinCutoff_(0) {
     if (queueCapacity == 0) {
+#if FOLLY_HAS_EXCEPTIONS
       throw std::invalid_argument(
           "MPMCQueue with explicit capacity 0 is impossible"
           // Stride computation in derived classes would sigfpe if capacity is 0
       );
+#endif
     }
+    FOLLY_SAFE_CHECK(queueCapacity > 0);
 
     // ideally this would be a static assert, but g++ doesn't allow it
     assert(
@@ -1371,42 +1371,24 @@
   }
 
   /// enqueue using in-place noexcept construction
-  template <
-      typename... Args,
-      typename = typename std::enable_if<
-          std::is_nothrow_constructible<T, Args...>::value>::type>
+  template <typename... Args>
   void enqueue(
       const uint32_t turn,
       Atom<uint32_t>& spinCutoff,
       const bool updateSpinCutoff,
       Args&&... args) noexcept {
+    static_assert(std::is_nothrow_constructible_v<T, Args...>);
     sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);
-    new (&contents_) T(std::forward<Args>(args)...);
+    new (&contents_) T(std::forward<Args>(args)...); // noexcept, per above
     sequencer_.completeTurn(turn * 2);
   }
 
-  /// enqueue using move construction, either real (if
-  /// is_nothrow_move_constructible) or simulated using relocation and
-  /// default construction (if IsRelocatable and is_nothrow_constructible)
-  template <
-      typename = typename std::enable_if<
-          (folly::IsRelocatable<T>::value &&
-           std::is_nothrow_constructible<T>::value) ||
-          std::is_nothrow_constructible<T, T&&>::value>::type>
   void enqueue(
       const uint32_t turn,
       Atom<uint32_t>& spinCutoff,
       const bool updateSpinCutoff,
       T&& goner) noexcept {
-    enqueueImpl(
-        turn,
-        spinCutoff,
-        updateSpinCutoff,
-        std::move(goner),
-        typename std::conditional<
-            std::is_nothrow_constructible<T, T&&>::value,
-            ImplByMove,
-            ImplByRelocation>::type());
+    enqueueImpl(turn, spinCutoff, updateSpinCutoff, std::move(goner));
   }
 
   /// Waits until either:
@@ -1433,15 +1415,7 @@
       Atom<uint32_t>& spinCutoff,
       const bool updateSpinCutoff,
       T& elem) noexcept {
-    dequeueImpl(
-        turn,
-        spinCutoff,
-        updateSpinCutoff,
-        elem,
-        typename std::conditional<
-            folly::IsRelocatable<T>::value,
-            ImplByRelocation,
-            ImplByMove>::type());
+    dequeueImpl(turn, spinCutoff, updateSpinCutoff, elem);
   }
 
   /// Waits until either:
@@ -1473,77 +1447,29 @@
   T* ptr() noexcept { return static_cast<T*>(static_cast<void*>(&contents_)); }
 
   void destroyContents() noexcept {
-    try {
-      ptr()->~T();
-    } catch (...) {
-      // g++ doesn't seem to have std::is_nothrow_destructible yet
-    }
+    ptr()->~T(); // assume noexcept
     if (kIsDebug) {
       memset(&contents_, 'Q', sizeof(T));
     }
   }
 
-  /// Tag classes for dispatching to enqueue/dequeue implementation.
-  struct ImplByRelocation {};
-  struct ImplByMove {};
-
   /// enqueue using nothrow move construction.
   void enqueueImpl(
       const uint32_t turn,
       Atom<uint32_t>& spinCutoff,
       const bool updateSpinCutoff,
-      T&& goner,
-      ImplByMove) noexcept {
+      T&& goner) noexcept {
     sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);
     new (&contents_) T(std::move(goner));
     sequencer_.completeTurn(turn * 2);
   }
 
-  /// enqueue by simulating nothrow move with relocation, followed by
-  /// default construction to a noexcept relocation.
-  void enqueueImpl(
-      const uint32_t turn,
-      Atom<uint32_t>& spinCutoff,
-      const bool updateSpinCutoff,
-      T&& goner,
-      ImplByRelocation) noexcept {
-    sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);
-    memcpy(
-        static_cast<void*>(&contents_),
-        static_cast<void const*>(&goner),
-        sizeof(T));
-    sequencer_.completeTurn(turn * 2);
-    new (&goner) T();
-  }
-
-  /// dequeue by destructing followed by relocation.  This version is preferred,
-  /// because as much work as possible can be done before waiting.
-  void dequeueImpl(
-      uint32_t turn,
-      Atom<uint32_t>& spinCutoff,
-      const bool updateSpinCutoff,
-      T& elem,
-      ImplByRelocation) noexcept {
-    try {
-      elem.~T();
-    } catch (...) {
-      // unlikely, but if we don't complete our turn the queue will die
-    }
-    sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff);
-    memcpy(
-        static_cast<void*>(&elem),
-        static_cast<void const*>(&contents_),
-        sizeof(T));
-    sequencer_.completeTurn(turn * 2 + 1);
-  }
-
   /// dequeue by nothrow move assignment.
   void dequeueImpl(
       uint32_t turn,
       Atom<uint32_t>& spinCutoff,
       const bool updateSpinCutoff,
-      T& elem,
-      ImplByMove) noexcept {
+      T& elem) noexcept {
     sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff);
     elem = std::move(*ptr());
     destroyContents();
diff --git a/folly/folly/MapUtil.h b/folly/folly/MapUtil.h
--- a/folly/folly/MapUtil.h
+++ b/folly/folly/MapUtil.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/container/MapUtil.h>
+#include <folly/container/MapUtil.h> // @shim
diff --git a/folly/folly/MaybeManagedPtr.h b/folly/folly/MaybeManagedPtr.h
--- a/folly/folly/MaybeManagedPtr.h
+++ b/folly/folly/MaybeManagedPtr.h
@@ -35,7 +35,13 @@
  public:
   /* implicit */ MaybeManagedPtr(T* t)
       : t_(std::shared_ptr<T>(std::shared_ptr<void>(), t)) {}
-  /* implicit */ MaybeManagedPtr(std::shared_ptr<T> t) : t_(t) {}
+  /* implicit */ MaybeManagedPtr(std::shared_ptr<T> t) : t_(std::move(t)) {}
+
+  /**
+   * Construction from shared_ptr<X>, where X is derived from T.
+   */
+  template <typename Q, typename = std::enable_if_t<std::is_base_of_v<T, Q>>>
+  /* implicit */ MaybeManagedPtr(std::shared_ptr<Q> q) : t_(std::move(q)) {}
 
   /**
    * Get pointer to the element contained in MaybeManagedPtr.
diff --git a/folly/folly/Memory.h b/folly/folly/Memory.h
--- a/folly/folly/Memory.h
+++ b/folly/folly/Memory.h
@@ -369,7 +369,7 @@
 namespace detail {
 void weak_ptr_set_stored_ptr(std::weak_ptr<void>& w, void* ptr);
 
-template <typename Tag, void* std::__weak_ptr<void>::*WeakPtr_Ptr_Field>
+template <typename Tag, void* std::__weak_ptr<void>::* WeakPtr_Ptr_Field>
 struct GenerateWeakPtrInternalsAccessor {
   friend void weak_ptr_set_stored_ptr(std::weak_ptr<void>& w, void* ptr) {
     w.*WeakPtr_Ptr_Field = ptr;
@@ -415,6 +415,50 @@
   return std::shared_ptr<U>(r, ptr);
 #endif
 }
+
+/**
+ * fmap_shared_ptr_aliasing
+ *
+ * This is a helper method that allows one to get aliased shared_ptr to an inner
+ * object inside another shared_ptr. For example, if you have an object of type
+ * T that contains field of type U, you can use this method to get a shared
+ * pointer to the field U by calling `fmap_shared_ptr_aliasing(ptrToT, getU)`
+ * where `getU` is a function that returns a pointer to that field.
+ * @param getU a function that returns a const pointer to the field of type U by
+ * taking a `const T*` as an argument.
+ * @return a shared_ptr to the field U or nullptr if the owner is
+ * nullptr or getU returns nullptr.
+ */
+template <
+    typename T,
+    typename GetU,
+    typename U = std::remove_pointer_t<std::invoke_result_t<GetU&, const T*>>>
+std::shared_ptr<U> fmap_shared_ptr_aliasing(
+    const std::shared_ptr<T>& owner, GetU getU) {
+  if (auto* tPtr = owner.get()) {
+    if (auto* uPtr = getU(tPtr)) {
+      return to_shared_ptr_aliasing(owner, uPtr);
+    }
+  }
+  return nullptr;
+}
+
+template <
+    typename T,
+    typename GetU,
+    typename U = std::remove_pointer_t<std::invoke_result_t<GetU&, const T*>>>
+std::shared_ptr<U> fmap_shared_ptr_aliasing(
+    std::shared_ptr<T>&& owner, GetU getU) {
+  if (auto* tPtr = owner.get()) {
+    if (auto* uPtr = getU(tPtr)) {
+      return to_shared_ptr_aliasing(owner, uPtr);
+    }
+  }
+  return nullptr;
+}
+
+template <typename GetU>
+auto fmap_shared_ptr_aliasing(std::nullptr_t owner, GetU&& getU) = delete;
 
 /**
  *  copy_to_unique_ptr
diff --git a/folly/folly/MicroLock.h b/folly/folly/MicroLock.h
--- a/folly/folly/MicroLock.h
+++ b/folly/folly/MicroLock.h
@@ -17,9 +17,7 @@
 #pragma once
 
 #include <cassert>
-#include <climits>
 #include <cstdint>
-#include <utility>
 
 #include <folly/Optional.h>
 #include <folly/Portability.h>
@@ -304,5 +302,5 @@
   }
 }
 
-typedef MicroLockBase<> MicroLock;
+using MicroLock = MicroLockBase<>;
 } // namespace folly
diff --git a/folly/folly/ObserverContainer.h b/folly/folly/ObserverContainer.h
--- a/folly/folly/ObserverContainer.h
+++ b/folly/folly/ObserverContainer.h
@@ -25,6 +25,7 @@
 #include <folly/Optional.h>
 #include <folly/ScopeGuard.h>
 #include <folly/io/async/DestructorCheck.h>
+#include <folly/lang/Switch.h>
 #include <folly/small_vector.h>
 
 /**
@@ -166,21 +167,25 @@
     if (iterating_) {
       CHECK(maybeCurrentIterationPolicy_.has_value());
       const auto& policy = maybeCurrentIterationPolicy_.value();
-      switch (policy) {
-        case InvokeWhileIteratingPolicy::InvokeAdded:
-        case InvokeWhileIteratingPolicy::DoNotInvokeAdded:
-          break;
-        case InvokeWhileIteratingPolicy::CheckNoChange:
-          folly::terminate_with<std::runtime_error>(
-              "Cannot add observers while iterating "
-              "per current iteration policy (CheckNoChange)");
-          break;
-        case InvokeWhileIteratingPolicy::CheckNoAdded:
-          folly::terminate_with<std::runtime_error>(
-              "Cannot add observers while iterating "
-              "per current iteration policy (CheckNoAdded)");
-          break;
-      }
+      FOLLY_EXHAUSTIVE_SWITCH({
+        switch (policy) {
+          case InvokeWhileIteratingPolicy::InvokeAdded:
+          case InvokeWhileIteratingPolicy::DoNotInvokeAdded:
+            break;
+          case InvokeWhileIteratingPolicy::CheckNoChange:
+            folly::terminate_with<std::runtime_error>(
+                "Cannot add observers while iterating "
+                "per current iteration policy (CheckNoChange)");
+            break;
+          case InvokeWhileIteratingPolicy::CheckNoAdded:
+            folly::terminate_with<std::runtime_error>(
+                "Cannot add observers while iterating "
+                "per current iteration policy (CheckNoAdded)");
+            break;
+          default:
+            folly::assume_unreachable();
+        }
+      });
     }
     observers_.insert(observers_.end(), observer);
     return true;
@@ -203,18 +208,22 @@
     if (iterating_) {
       CHECK(maybeCurrentIterationPolicy_.has_value());
       const auto& policy = maybeCurrentIterationPolicy_.value();
-      switch (policy) {
-        case InvokeWhileIteratingPolicy::InvokeAdded:
-        case InvokeWhileIteratingPolicy::DoNotInvokeAdded:
-          break;
-        case InvokeWhileIteratingPolicy::CheckNoChange:
-          folly::terminate_with<std::runtime_error>(
-              "Cannot remove observers while iterating "
-              "per current iteration policy (CheckNoChange)");
-          break;
-        case InvokeWhileIteratingPolicy::CheckNoAdded:
-          break;
-      }
+      FOLLY_EXHAUSTIVE_SWITCH({
+        switch (policy) {
+          case InvokeWhileIteratingPolicy::InvokeAdded:
+          case InvokeWhileIteratingPolicy::DoNotInvokeAdded:
+            break;
+          case InvokeWhileIteratingPolicy::CheckNoChange:
+            folly::terminate_with<std::runtime_error>(
+                "Cannot remove observers while iterating "
+                "per current iteration policy (CheckNoChange)");
+            break;
+          case InvokeWhileIteratingPolicy::CheckNoAdded:
+            break;
+          default:
+            folly::assume_unreachable();
+        }
+      });
 
       *it = nullptr;
       removalDuringIteration_ = true;
diff --git a/folly/folly/OperationCancelled.h b/folly/folly/OperationCancelled.h
--- a/folly/folly/OperationCancelled.h
+++ b/folly/folly/OperationCancelled.h
@@ -22,7 +22,7 @@
 
 /// IMPORTANT: `folly`-internal, do NOT use this in new user code. Instead:
 ///
-///   - `co_yield coro::co_cancelled` to signal that a coro was cancelled.
+///   - `co_yield coro::co_stopped_may_throw` to signal a coro was cancelled.
 ///
 ///   - To check for cancellation in `folly::coro` coroutines, use one of:
 ///       // (1) default behavior
@@ -31,7 +31,7 @@
 ///       auto& ctok = co_await coro::co_current_cancellation_token;
 ///       if (ctok.isCancellationRequested()) {
 ///         /* ... do stuff ... */
-///         co_yield coro::co_cancelled;
+///         co_yield coro::co_stopped_may_throw;
 ///       }
 ///
 ///   - Store `stopped_result` to obtain a `result<T>` or `non_value_result`
@@ -40,22 +40,32 @@
 ///   - To avoid depending on `OperationCancelled` in code that would do
 ///     `try-catch` in `folly::coro` coroutines, do this instead:
 ///
-///       auto res = co_await coro::co_await_result(mightGetCancelled());
-///       if (auto* ex = get_exception<MyErr>(res)) {
-///         // HANDLE ERROR HERE
+///       auto res = co_await coro::value_or_error_or_stopped(
+///           mightGetCancelled());
+///       if (auto ex = get_exception<MyErr>(res)) {
+///         // Handle error here; `ex` quacks like `const MyErr*`.
 ///       } else if (res.has_stopped()) {
-///         // HANDLE CANCELLATION HERE
-///         co_yield coro::co_cancelled;
+///         // Handle cancellation here
+///         co_yield coro::co_stopped_may_throw;
 ///       } else { // get value, or propagate unhandled errors/cancellation
-///         auto v = co_await coro::co_ready(std::move(res));
+///         auto v = co_await folly::or_unwind(std::move(res));
 ///       }
 ///
+///     OR: If you want to transparently propagate cancellation to the parent
+///     coro, use `value_or_error()` instead.  Then, a "cancellation" /
+///     "stopped" completion will promptly end the current coro WITHOUT
+///     throwing, and its parent will see a "stopped" completion as well.  As
+///     with `co_nothrow`, ask for help in Coroutines@ if your current scope
+///     requires async cleanup / async RAII (such as "async scope" usage or
+///     other background work).
+///
 /// Rationale / purpose: For now, `folly` uses the `OperationCancelled`
 /// exception to signal "this work was stopped".  However, as of C++26 (see
-/// P2300, plus arguments in P1677), standard C++ differentiates between
-/// "value", "error", and "stopped" completions.  Therefore, we ask end-user
-/// code to use the above cancellation-specific constructs, WITHOUT assuming
-/// that cancellation / stopping is implemented as an exception.
+/// https://wg21.link/P2300, per the arguments in https://wg21.link/P1677),
+/// standard C++ differentiates between "value", "error", and "stopped"
+/// completions.  Therefore, we ask end-user code to use the above
+/// cancellation-specific constructs, WITHOUT assuming that cancellation /
+/// stopping is implemented as an exception.
 struct OperationCancelled final : public std::exception {
   const char* what() const noexcept override {
     return "coroutine operation cancelled";
diff --git a/folly/folly/Optional.h b/folly/folly/Optional.h
--- a/folly/folly/Optional.h
+++ b/folly/folly/Optional.h
@@ -110,7 +110,7 @@
 template <class Value>
 class Optional {
  public:
-  typedef Value value_type;
+  using value_type = Value;
 
   using promise_type = detail::OptionalPromise<Value>;
 
@@ -220,11 +220,19 @@
   }
 
   std::optional<Value> toStdOptional() && noexcept {
-    return static_cast<std::optional<Value>>(std::move(*this));
+    if (has_value()) {
+      auto opt = std::optional<Value>(std::move(value()));
+      reset();
+      return opt;
+    }
+    return std::nullopt;
   }
 
   std::optional<Value> toStdOptional() const& noexcept {
-    return static_cast<std::optional<Value>>(*this);
+    if (has_value()) {
+      return std::optional<Value>(value());
+    }
+    return std::nullopt;
   }
 
   /// Set the Optional
@@ -477,7 +485,7 @@
     };
     bool hasValue;
 
-    FOLLY_CXX20_CONSTEXPR StorageNonTriviallyDestructible() : hasValue{false} {}
+    constexpr StorageNonTriviallyDestructible() : hasValue{false} {}
     ~StorageNonTriviallyDestructible() { clear(); }
 
     void clear() {
@@ -736,7 +744,8 @@
 
   // Explicitly only allow suspension into an OptionalPromise
   template <typename U>
-  void await_suspend(coro::coroutine_handle<OptionalPromise<U>> h) const {
+  void await_suspend(
+      coro::coroutine_handle<OptionalPromise<U>> h) const noexcept {
     // Abort the rest of the coroutine. resume() is not going to be called
     h.destroy();
   }
diff --git a/folly/folly/PackedSyncPtr.h b/folly/folly/PackedSyncPtr.h
--- a/folly/folly/PackedSyncPtr.h
+++ b/folly/folly/PackedSyncPtr.h
@@ -65,7 +65,7 @@
   // This just allows using this class even with T=void.  Attempting
   // to use the operator* or operator[] on a PackedSyncPtr<void> will
   // still properly result in a compile error.
-  typedef typename std::add_lvalue_reference<T>::type reference;
+  using reference = typename std::add_lvalue_reference<T>::type;
 
  public:
   /*
diff --git a/folly/folly/Padded.h b/folly/folly/Padded.h
--- a/folly/folly/Padded.h
+++ b/folly/folly/Padded.h
@@ -61,7 +61,7 @@
       std::is_trivial_v<T> && sizeof(T) <= NS && NS % alignof(T) == 0);
 
  public:
-  typedef T value_type;
+  using value_type = T;
   static constexpr size_t kNodeSize = NS;
   static constexpr size_t kElementCount = NS / sizeof(T);
   static constexpr size_t kPaddingBytes = NS % sizeof(T);
@@ -118,15 +118,6 @@
   } storage_;
 };
 
-// We must define kElementCount and kPaddingBytes to work around a bug
-// in gtest that odr-uses them.
-template <class T, size_t NS>
-constexpr size_t Node<T, NS>::kNodeSize;
-template <class T, size_t NS>
-constexpr size_t Node<T, NS>::kElementCount;
-template <class T, size_t NS>
-constexpr size_t Node<T, NS>::kPaddingBytes;
-
 template <class Iter>
 class Iterator;
 
@@ -137,8 +128,7 @@
 // Helper class template to define a base class for Iterator (below) and save
 // typing.
 template <
-    template <class>
-    class Class,
+    template <class> class Class,
     class Iter,
     class Traits = std::iterator_traits<Iter>,
     class Ref = typename Traits::reference,
@@ -286,14 +276,14 @@
 template <class Container>
 class Adaptor {
  public:
-  typedef typename Container::value_type Node;
-  typedef typename Node::value_type value_type;
-  typedef value_type& reference;
-  typedef const value_type& const_reference;
-  typedef Iterator<typename Container::iterator> iterator;
-  typedef Iterator<typename Container::const_iterator> const_iterator;
-  typedef typename const_iterator::difference_type difference_type;
-  typedef typename Container::size_type size_type;
+  using Node = typename Container::value_type;
+  using value_type = typename Node::value_type;
+  using reference = value_type&;
+  using const_reference = const value_type&;
+  using iterator = Iterator<typename Container::iterator>;
+  using const_iterator = Iterator<typename Container::const_iterator>;
+  using difference_type = typename const_iterator::difference_type;
+  using size_type = typename Container::size_type;
 
   static constexpr size_t kElementsPerNode = Node::kElementCount;
   // Constructors
diff --git a/folly/folly/Poly.h b/folly/folly/Poly.h
--- a/folly/folly/Poly.h
+++ b/folly/folly/Poly.h
@@ -216,9 +216,8 @@
     class Poly,
     typename... As,
     std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0>
-auto poly_call(Poly&& _this, As&&... as)
-    -> decltype(poly_call<N, I>(
-        static_cast<Poly&&>(_this).get(), static_cast<As&&>(as)...)) {
+auto poly_call(Poly&& _this, As&&... as) -> decltype(poly_call<N, I>(
+    static_cast<Poly&&>(_this).get(), static_cast<As&&>(as)...)) {
   return poly_call<N, I>(
       static_cast<Poly&&>(_this).get(), static_cast<As&&>(as)...);
 }
diff --git a/folly/folly/Portability.h b/folly/folly/Portability.h
--- a/folly/folly/Portability.h
+++ b/folly/folly/Portability.h
@@ -78,22 +78,6 @@
   __attribute__((__format__(__printf__, format_param, dots_param)))
 #endif
 
-// warn unused result
-#if defined(__has_cpp_attribute)
-#if __has_cpp_attribute(nodiscard)
-#if defined(__clang__) || defined(__GNUC__)
-#if __clang_major__ >= 10 || __GNUC__ >= 10
-// early clang and gcc both warn on [[nodiscard]] when applied to class ctors
-// easiest option is just to avoid emitting [[nodiscard]] under early clang/gcc
-#define FOLLY_NODISCARD [[nodiscard]]
-#endif
-#endif
-#endif
-#endif
-#ifndef FOLLY_NODISCARD
-#define FOLLY_NODISCARD
-#endif
-
 // older clang-format gets confused by [[deprecated(...)]] on class decls
 #define FOLLY_DEPRECATED(...) [[deprecated(__VA_ARGS__)]]
 
@@ -104,6 +88,13 @@
 #define FOLLY_TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))
 #endif
 
+#if defined(__i386__) || defined(__i686__) || defined(__x86__) || \
+    defined(_M_IX86)
+#define FOLLY_X86 1
+#else
+#define FOLLY_X86 0
+#endif
+
 // detection for 64 bit
 #if defined(__x86_64__) || defined(_M_X64)
 #define FOLLY_X64 1
@@ -149,6 +140,7 @@
 
 namespace folly {
 constexpr bool kIsArchArm = FOLLY_ARM == 1;
+constexpr bool kIsArchX86 = FOLLY_X86 == 1;
 constexpr bool kIsArchAmd64 = FOLLY_X64 == 1;
 constexpr bool kIsArchAArch64 = FOLLY_AARCH64 == 1;
 constexpr bool kIsArchPPC64 = FOLLY_PPC64 == 1;
@@ -499,12 +491,14 @@
 constexpr auto kIsMobile = false;
 #endif
 
-#if defined(__linux__) && !FOLLY_MOBILE
-constexpr auto kIsLinux = true;
+#if defined(__linux__)
+constexpr auto kIsLinuxActual = true;
 #else
-constexpr auto kIsLinux = false;
+constexpr auto kIsLinuxActual = false;
 #endif
 
+constexpr auto kIsLinux = kIsLinuxActual && !kIsMobile;
+
 #if defined(__FreeBSD__)
 constexpr auto kIsFreeBSD = true;
 #else
@@ -517,6 +511,12 @@
 constexpr auto kIsWindows = false;
 #endif
 
+#if defined(__ANDROID__)
+constexpr auto kIsAndroid = true;
+#else
+constexpr auto kIsAndroid = false;
+#endif
+
 #if defined(__APPLE__)
 constexpr auto kIsApple = true;
 #else
@@ -620,16 +620,6 @@
 #define FOLLY_STORAGE_CONSTEXPR constexpr
 #endif
 
-//  FOLLY_CXX20_CONSTEXPR
-//
-//  C++20 permits more cases to be marked constexpr, including constructors that
-//  leave members uninitialized and virtual functions.
-#if FOLLY_CPLUSPLUS >= 202002L
-#define FOLLY_CXX20_CONSTEXPR constexpr
-#else
-#define FOLLY_CXX20_CONSTEXPR
-#endif
-
 //  FOLLY_CXX23_CONSTEXPR
 //
 //  C++23 permits more cases to be marked constexpr, including definitions of
@@ -699,9 +689,13 @@
 //  - 14 is still bad: https://godbolt.org/z/nW1W8cWvb
 //  - 15.0.0 is good: https://godbolt.org/z/Tco4c9hbq and sEaKKTf8r
 #define FOLLY_HAS_IMMOVABLE_COROUTINES 0
+// On Windows, Clang with -fms-compatibility defines _MSC_FULL_VER to
+// emulate MSVC - even for newer versions of Clang.
+// Explicitly allow Clang 15+, prior to checking _MSC_FULL_VER
+#elif defined(__clang_major__) && __clang_major__ >= 15
+#define FOLLY_HAS_IMMOVABLE_COROUTINES 1
 // BEWARE: Older versions of Clang pretend to be MSVC and define
-// `_MSC_FULL_VER`, but fortunately none of clang 15, 16, 17, 18, 19 do this,
-// so this branch should not result in a false-negative.
+// `_MSC_FULL_VER`
 #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 192930040
 //  - 192930040 is bad: https://godbolt.org/z/E797W8xTT
 //  - 192930153 is good: https://godbolt.org/z/cM4nW5rTK
diff --git a/folly/folly/ProducerConsumerQueue.h b/folly/folly/ProducerConsumerQueue.h
--- a/folly/folly/ProducerConsumerQueue.h
+++ b/folly/folly/ProducerConsumerQueue.h
@@ -34,7 +34,7 @@
  */
 template <class T>
 struct ProducerConsumerQueue {
-  typedef T value_type;
+  using value_type = T;
 
   ProducerConsumerQueue(const ProducerConsumerQueue&) = delete;
   ProducerConsumerQueue& operator=(const ProducerConsumerQueue&) = delete;
diff --git a/folly/folly/Random-inl.h b/folly/folly/Random-inl.h
--- a/folly/folly/Random-inl.h
+++ b/folly/folly/Random-inl.h
@@ -22,65 +22,28 @@
 
 namespace detail {
 
-// Return the state size needed by RNG, expressed as a number of uint32_t
-// integers. Specialized for all templates specified in the C++11 standard.
-// For some (mersenne_twister_engine), this is exported as a state_size static
-// data member; for others, the standard shows formulas.
-
-template <class RNG, typename = void>
-struct StateSize {
-  // A sane default.
-  using type = std::integral_constant<size_t, 512>;
-};
-
-template <class RNG>
-struct StateSize<RNG, void_t<decltype(RNG::state_size)>> {
-  using type = std::integral_constant<size_t, RNG::state_size>;
-};
-
-template <class UIntType, UIntType a, UIntType c, UIntType m>
-struct StateSize<std::linear_congruential_engine<UIntType, a, c, m>> {
-  // From the standard [rand.eng.lcong], this is ceil(log2(m) / 32) + 3,
-  // which is the same as ceil(ceil(log2(m) / 32) + 3, and
-  // ceil(log2(m)) <= std::numeric_limits<UIntType>::digits
-  using type = std::integral_constant<
-      size_t,
-      (std::numeric_limits<UIntType>::digits + 31) / 32 + 3>;
-};
-
-template <class UIntType, size_t w, size_t s, size_t r>
-struct StateSize<std::subtract_with_carry_engine<UIntType, w, s, r>> {
-  // [rand.eng.sub]: r * ceil(w / 32)
-  using type = std::integral_constant<size_t, r*((w + 31) / 32)>;
-};
-
-template <typename RNG>
-using StateSizeT = _t<StateSize<RNG>>;
-
-template <class RNG>
-struct SeedData {
-  SeedData() {
-    Random::secureRandom(seedData.data(), seedData.size() * sizeof(uint32_t));
+struct SeedSeqSecureRandom {
+  using result_type = uint32_t;
+  template <typename Word>
+  void generate(Word* b, Word* e) {
+    static_assert(is_non_bool_integral_v<Word>);
+    static_assert(sizeof(Word) >= sizeof(result_type));
+    Random::secureRandom(b, (e - b) * sizeof(Word));
   }
-
-  static constexpr size_t stateSize = StateSizeT<RNG>::value;
-  std::array<uint32_t, stateSize> seedData;
 };
 
 } // namespace detail
 
 template <class RNG, class /* EnableIf */>
 void Random::seed(RNG& rng) {
-  detail::SeedData<RNG> sd;
-  std::seed_seq s(std::begin(sd.seedData), std::end(sd.seedData));
-  rng.seed(s);
+  detail::SeedSeqSecureRandom seq;
+  rng.seed(seq);
 }
 
 template <class RNG, class /* EnableIf */>
 auto Random::create() -> RNG {
-  detail::SeedData<RNG> sd;
-  std::seed_seq s(std::begin(sd.seedData), std::end(sd.seedData));
-  return RNG(s);
+  detail::SeedSeqSecureRandom seq;
+  return RNG(seq);
 }
 
 } // namespace folly
diff --git a/folly/folly/Range.h b/folly/folly/Range.h
--- a/folly/folly/Range.h
+++ b/folly/folly/Range.h
@@ -21,7 +21,7 @@
 /**
  * Range abstraction using a pair of iterators. It is not
  * similar to boost's range abstraction because an API identical
- * with the former StringPiece class is required, which is used alot
+ * with the former StringPiece class is required, which is used a lot
  * internally. This abstraction does fulfill the needs of boost's
  * range-oriented algorithms though.
  *
@@ -152,6 +152,19 @@
   using type = int;
 };
 
+#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
+template <>
+struct IsCharPointer<char8_t*> {
+  using type = int;
+};
+
+template <>
+struct IsCharPointer<const char8_t*> {
+  using const_type = int;
+  using type = int;
+};
+#endif
+
 template <class T>
 struct IsUnsignedCharPointer {};
 
@@ -577,7 +590,7 @@
   }
 
   /**
-   * Reset start and end interator based on size
+   * Reset start and end iterator based on size
    */
   void reset(Iter start, size_type size) {
     b_ = start;
@@ -957,7 +970,7 @@
     return find(other) != std::string::npos;
   }
 
-  void swap(Range& rhs) {
+  void swap(Range& rhs) noexcept(std::is_nothrow_swappable_v<Iter>) {
     std::swap(b_, rhs.b_);
     std::swap(e_, rhs.e_);
   }
@@ -1262,7 +1275,8 @@
 const typename Range<Iter>::size_type Range<Iter>::npos = std::string::npos;
 
 template <class Iter>
-void swap(Range<Iter>& lhs, Range<Iter>& rhs) {
+void swap(Range<Iter>& lhs, Range<Iter>& rhs) noexcept(
+    noexcept(lhs.swap(rhs))) {
   lhs.swap(rhs);
 }
 
diff --git a/folly/folly/Replaceable.h b/folly/folly/Replaceable.h
--- a/folly/folly/Replaceable.h
+++ b/folly/folly/Replaceable.h
@@ -157,8 +157,9 @@
   dtor_mixin& operator=(dtor_mixin&&) = default;
   dtor_mixin& operator=(dtor_mixin const&) = default;
   ~dtor_mixin() noexcept(std::is_nothrow_destructible<T>::value) {
-    T* destruct_ptr = std::launder(reinterpret_cast<T*>(
-        reinterpret_cast<Replaceable<T>*>(this)->storage_));
+    T* destruct_ptr = std::launder(
+        reinterpret_cast<T*>(
+            reinterpret_cast<Replaceable<T>*>(this)->storage_));
     destruct_ptr->~T();
   }
 };
@@ -282,8 +283,9 @@
   operator=(move_assignment_mixin&& other) noexcept(
       std::is_nothrow_destructible<T>::value &&
       std::is_nothrow_move_constructible<T>::value) {
-    T* destruct_ptr = std::launder(reinterpret_cast<T*>(
-        reinterpret_cast<Replaceable<T>*>(this)->storage_));
+    T* destruct_ptr = std::launder(
+        reinterpret_cast<T*>(
+            reinterpret_cast<Replaceable<T>*>(this)->storage_));
     destruct_ptr->~T();
     ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
         T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
@@ -346,8 +348,9 @@
   operator=(copy_assignment_mixin const& other) noexcept(
       std::is_nothrow_destructible<T>::value &&
       std::is_nothrow_copy_constructible<T>::value) {
-    T* destruct_ptr = std::launder(reinterpret_cast<T*>(
-        reinterpret_cast<Replaceable<T>*>(this)->storage_));
+    T* destruct_ptr = std::launder(
+        reinterpret_cast<T*>(
+            reinterpret_cast<Replaceable<T>*>(this)->storage_));
     destruct_ptr->~T();
     ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
         T(*reinterpret_cast<Replaceable<T> const&>(other));
diff --git a/folly/folly/ScopeGuard.h b/folly/folly/ScopeGuard.h
--- a/folly/folly/ScopeGuard.h
+++ b/folly/folly/ScopeGuard.h
@@ -209,7 +209,7 @@
  * @refcode folly/docs/examples/folly/ScopeGuard2.cpp
  */
 template <typename F>
-FOLLY_NODISCARD detail::ScopeGuardImplDecay<F, true> makeGuard(F&& f) noexcept(
+[[nodiscard]] detail::ScopeGuardImplDecay<F, true> makeGuard(F&& f) noexcept(
     noexcept(detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f)))) {
   return detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f));
 }
@@ -223,7 +223,7 @@
  * @refcode folly/docs/examples/folly/ScopeGuard2.cpp
  */
 template <typename F>
-FOLLY_NODISCARD detail::ScopeGuardImplDecay<F, true>
+[[nodiscard]] detail::ScopeGuardImplDecay<F, true>
 makeDismissedGuard(F&& f) noexcept(
     noexcept(detail::ScopeGuardImplDecay<F, true>(
         static_cast<F&&>(f), detail::ScopeGuardDismissed{}))) {
diff --git a/folly/folly/SharedMutex.h b/folly/folly/SharedMutex.h
--- a/folly/folly/SharedMutex.h
+++ b/folly/folly/SharedMutex.h
@@ -63,9 +63,9 @@
 // reader-writer locks in use at Facebook for almost all use cases,
 // sometimes by a wide margin.  (If it is rare that there are actually
 // concurrent readers then RWSpinLock can be a few nanoseconds faster.)
-// I compared it to folly::RWSpinLock, folly::RWTicketSpinLock64,
-// boost::shared_mutex, pthread_rwlock_t, and a RWLock that internally uses
-// spinlocks to guard state and pthread_mutex_t+pthread_cond_t to block.
+// I compared it to folly::RWSpinLock, boost::shared_mutex,
+// pthread_rwlock_t, and a RWLock that internally uses spinlocks to guard
+// state and pthread_mutex_t+pthread_cond_t to block.
 // (Thrift's ReadWriteMutex is based underneath on pthread_rwlock_t.)
 // It is generally as good or better than the rest when evaluating size,
 // speed, scalability, or latency outliers.  In the corner cases where
@@ -962,9 +962,7 @@
   // that are the same after integer division by k share that resource.
   // Our strategy for deferred readers is to probe up to numSlots/4 slots,
   // using the full granularity of AccessSpreader for the start slot
-  // and then search outward.  We can use AccessSpreader::current(n)
-  // without managing our own spreader if kMaxDeferredReaders <=
-  // AccessSpreader::kMaxCpus, which is currently 128.
+  // and then search outward.
   //
   // In order to give each L1 cache its own playground, we need
   // kMaxDeferredReaders >= #L1 caches. We double it, making it
@@ -1439,8 +1437,7 @@
 template <
     bool ReaderPriority,
     typename Tag_,
-    template <typename>
-    class Atom,
+    template <typename> class Atom,
     typename Policy>
 alignas(hardware_destructive_interference_size)
     typename SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>::
@@ -1452,8 +1449,7 @@
 template <
     bool ReaderPriority,
     typename Tag_,
-    template <typename>
-    class Atom,
+    template <typename> class Atom,
     typename Policy>
 bool SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>::
     tryUnlockTokenlessSharedDeferred() {
@@ -1477,8 +1473,7 @@
 template <
     bool ReaderPriority,
     typename Tag_,
-    template <typename>
-    class Atom,
+    template <typename> class Atom,
     typename Policy>
 template <class WaitContext>
 bool SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>::lockSharedImpl(
@@ -1621,8 +1616,7 @@
 template <
     bool ReaderPriority,
     typename Tag_,
-    template <typename>
-    class Atom,
+    template <typename> class Atom,
     typename Policy>
 class shared_lock<
     ::folly::SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>> {
@@ -1633,7 +1627,7 @@
 
   shared_lock() noexcept = default;
 
-  FOLLY_NODISCARD explicit shared_lock(mutex_type& mutex)
+  [[nodiscard]] explicit shared_lock(mutex_type& mutex)
       : mutex_(std::addressof(mutex)) {
     lock();
   }
@@ -1641,18 +1635,18 @@
   shared_lock(mutex_type& mutex, std::defer_lock_t) noexcept
       : mutex_(std::addressof(mutex)) {}
 
-  FOLLY_NODISCARD shared_lock(mutex_type& mutex, std::try_to_lock_t)
+  [[nodiscard]] shared_lock(mutex_type& mutex, std::try_to_lock_t)
       : mutex_(std::addressof(mutex)) {
     try_lock();
   }
 
-  FOLLY_NODISCARD shared_lock(mutex_type& mutex, std::adopt_lock_t)
+  [[nodiscard]] shared_lock(mutex_type& mutex, std::adopt_lock_t)
       : mutex_(std::addressof(mutex)) {
     token_.state_ = token_type::State::LockedShared;
   }
 
   template <typename Clock, typename Duration>
-  FOLLY_NODISCARD shared_lock(
+  [[nodiscard]] shared_lock(
       mutex_type& mutex,
       const std::chrono::time_point<Clock, Duration>& deadline)
       : mutex_(std::addressof(mutex)) {
@@ -1660,7 +1654,7 @@
   }
 
   template <typename Rep, typename Period>
-  FOLLY_NODISCARD shared_lock(
+  [[nodiscard]] shared_lock(
       mutex_type& mutex, const std::chrono::duration<Rep, Period>& timeout)
       : mutex_(std::addressof(mutex)) {
     try_lock_for(timeout);
@@ -1727,13 +1721,13 @@
     return std::exchange(mutex_, nullptr);
   }
 
-  FOLLY_NODISCARD bool owns_lock() const noexcept {
+  [[nodiscard]] bool owns_lock() const noexcept {
     return static_cast<bool>(token_);
   }
 
   explicit operator bool() const noexcept { return owns_lock(); }
 
-  FOLLY_NODISCARD mutex_type* mutex() const noexcept { return mutex_; }
+  [[nodiscard]] mutex_type* mutex() const noexcept { return mutex_; }
 
  private:
   void error_if_not_lockable() const {
diff --git a/folly/folly/Singleton-inl.h b/folly/folly/Singleton-inl.h
--- a/folly/folly/Singleton-inl.h
+++ b/folly/folly/Singleton-inl.h
@@ -240,34 +240,52 @@
     detail::singletonWarnCreateCircularDependencyAndAbort(type());
   }
 
-  std::lock_guard entry_lock(mutex_);
-  if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) {
-    return;
-  }
-  if (state_.load(std::memory_order_relaxed) ==
-      SingletonHolderState::LivingInChildAfterFork) {
-    if (vault_.failOnUseAfterFork_) {
-      LOG(DFATAL) << "Attempting to use singleton " << type().name()
-                  << " in child process after fork";
-    } else {
-      LOG(ERROR) << "Attempting to use singleton " << type().name()
-                 << " in child process after fork";
-    }
-    auto expected = SingletonHolderState::LivingInChildAfterFork;
-    state_.compare_exchange_strong(
-        expected,
-        SingletonHolderState::Living,
-        std::memory_order_relaxed,
-        std::memory_order_relaxed);
+  // NOTE: Keeping the vault state lock at the top of the createInstance
+  // function is important to not deadlock singleton creation in child
+  // processes.
+  //
+  // We take and hold the vault state lock at the start to ensure that a write
+  // lock for the vault state isn't taken during the time that this Singleton's
+  // mutex_ is held, as it may result in a deadlock in creation of this
+  // singleton in the future if a fork happens in-between the mutex_ being
+  // acquired and the completion of this function that could "easily" happen if
+  // there's contention on the vault state, as another thread may initiate to
+  // destroyInstances in preparation for a fork
+  //
+  // This still doesn't 100% remove the risk of deadlock in child processes, as
+  // it's possible that the vault state rlock here is acquired in another
+  // thread before the fork happens. That would block the child process from
+  // re-instantiating the singleton vault, as it would never be able to get a
+  // write lock on it.
+  auto vault_state = vault_.state_.rlock();
+  if (vault_state->state == detail::SingletonVaultState::Type::Quiescing) {
     return;
   }
-  if (state_.load(std::memory_order_acquire) ==
-      SingletonHolderState::NotRegistered) {
-    detail::singletonWarnCreateUnregisteredAndAbort(type());
-  }
 
-  if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) {
-    return;
+  std::lock_guard entry_lock(mutex_);
+  switch (auto state = state_.load(std::memory_order_acquire)) {
+    case SingletonHolderState::Living:
+      return; // nothing to do
+    case SingletonHolderState::LivingInChildAfterFork:
+      if (vault_.failOnUseAfterFork_) {
+        LOG(DFATAL) << "Attempting to use singleton " << type().name()
+                    << " in child process after fork";
+      } else {
+        LOG(ERROR) << "Attempting to use singleton " << type().name()
+                   << " in child process after fork";
+      }
+      state_.compare_exchange_strong(
+          state,
+          SingletonHolderState::Living,
+          std::memory_order_relaxed,
+          std::memory_order_relaxed);
+      return; // just need to do the state transition
+    case SingletonHolderState::NotRegistered:
+      detail::singletonWarnCreateUnregisteredAndAbort(type()); // noreturn
+    case SingletonHolderState::Dead:
+      break; // recreate it below
+    default:
+      assume_unreachable(); // impossible state
   }
 
   SCOPE_EXIT {
@@ -279,14 +297,10 @@
 
   creating_thread_.store(std::this_thread::get_id(), std::memory_order_release);
 
-  auto state = vault_.state_.rlock();
   if (vault_.type_.load(std::memory_order_relaxed) !=
           SingletonVault::Type::Relaxed &&
-      !state->registrationComplete) {
+      !vault_state->registrationComplete) {
     detail::singletonWarnCreateBeforeRegistrationCompleteAndAbort(type());
-  }
-  if (state->state == detail::SingletonVaultState::Type::Quiescing) {
-    return;
   }
 
   auto destroy_baton = std::make_shared<folly::Baton<>>();
diff --git a/folly/folly/Singleton.cpp b/folly/folly/Singleton.cpp
--- a/folly/folly/Singleton.cpp
+++ b/folly/folly/Singleton.cpp
@@ -36,6 +36,7 @@
 #include <folly/lang/SafeAssert.h>
 #include <folly/portability/Config.h>
 #include <folly/portability/FmtCompile.h>
+#include <folly/system/AtFork.h>
 // Before registrationComplete() we cannot assume that glog has been
 // initialized, so we need to use RAW_LOG for any message that may be logged
 // before that.
@@ -43,6 +44,8 @@
 
 #if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__)
 #define FOLLY_SINGLETON_HAVE_DLSYM 1
+#else
+#define FOLLY_SINGLETON_HAVE_DLSYM 0
 #endif
 
 namespace folly {
@@ -442,7 +445,7 @@
 
 void SingletonVault::scheduleDestroyInstances() {
   // Add a dependency on folly::ThreadLocal to make sure all its static
-  // singletons are initalized first.
+  // singletons are initialized first.
   threadlocal_detail::StaticMeta<void, void>::instance();
 #if !defined(FOLLY_SINGLETON_SKIP_SCHEDULE_ATEXIT) || \
     !FOLLY_SINGLETON_SKIP_SCHEDULE_ATEXIT
diff --git a/folly/folly/SingletonThreadLocal.h b/folly/folly/SingletonThreadLocal.h
--- a/folly/folly/SingletonThreadLocal.h
+++ b/folly/folly/SingletonThreadLocal.h
@@ -100,7 +100,7 @@
 template <
     typename T,
     typename Tag = detail::DefaultTag,
-    typename Make = detail::DefaultMake<T>,
+    typename Make = void,
     typename TLTag = std::
         conditional_t<std::is_same<Tag, detail::DefaultTag>::value, void, Tag>>
 class SingletonThreadLocal {
@@ -110,12 +110,14 @@
   using State = detail::SingletonThreadLocalState;
   using LocalCache = State::LocalCache;
 
-  using Object = invoke_result_t<Make>;
+  using MakeFn =
+      std::conditional_t<std::is_void_v<Make>, detail::DefaultMake<T>, Make>;
+  using Object = invoke_result_t<MakeFn>;
   static_assert(std::is_convertible<Object&, T&>::value, "inconvertible");
 
   struct ObjectWrapper {
     // keep as first field in first base, to save 1 instr in the fast path
-    Object object{Make{}()};
+    Object object{MakeFn{}()};
   };
   struct Wrapper : ObjectWrapper, State::Tracking {
     /* implicit */ operator T&() { return ObjectWrapper::object; }
@@ -146,17 +148,31 @@
     return wrapper;
   }
 
+  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static LocalCache& getLocalCache() {
+    static thread_local LocalCache cache;
+    return cache;
+  }
+
  public:
-  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& get() {
+  FOLLY_ALWAYS_INLINE static T& get() {
     if (kIsMobile) {
       return getWrapper();
     }
-    static thread_local LocalCache cache;
+    auto& cache = getLocalCache();
     auto* object = static_cast<Object*>(cache.object);
     return FOLLY_LIKELY(!!object) ? *object : getSlow(cache).object;
   }
 
   static T* try_get() {
+    if (!kIsMobile) {
+      auto& cache = getLocalCache();
+      if (auto* object = static_cast<Object*>(cache.object)) {
+        return object;
+      }
+    }
+    if (threadlocal_detail::StaticMetaBase::dying()) {
+      return nullptr;
+    }
     auto* wrapper = getWrapperTL().get_existing();
     return wrapper ? &static_cast<T&>(*wrapper) : nullptr;
   }
diff --git a/folly/folly/SocketAddress.cpp b/folly/folly/SocketAddress.cpp
--- a/folly/folly/SocketAddress.cpp
+++ b/folly/folly/SocketAddress.cpp
@@ -295,10 +295,11 @@
     return;
 #endif
   } else {
-    throw std::invalid_argument(fmt::format(
-        "SocketAddress::setFromSockaddr() called "
-        "with unsupported address type {}",
-        address->sa_family));
+    throw std::invalid_argument(
+        fmt::format(
+            "SocketAddress::setFromSockaddr() called "
+            "with unsupported address type {}",
+            address->sa_family));
   }
 
   // For IP addresses, use the IPAddress constructor
diff --git a/folly/folly/SocketAddress.h b/folly/folly/SocketAddress.h
--- a/folly/folly/SocketAddress.h
+++ b/folly/folly/SocketAddress.h
@@ -18,7 +18,6 @@
 
 #include <sys/types.h>
 
-#include <cassert>
 #include <cstddef>
 #include <iosfwd>
 #include <string>
@@ -360,6 +359,34 @@
   static SocketAddress makeFromPath(StringPiece path) {
     SocketAddress addr;
     addr.setFromPath(path);
+    return addr;
+  }
+
+  /**
+   * Construct a SocketAddress from a local port number.
+   * see setFromLocalPort for more details.
+   *
+   * Raises std::invalid_argument on error.
+   *
+   * @param port The local port number (in host byte order)
+   */
+  static SocketAddress makeFromLocalPort(uint16_t port) {
+    SocketAddress addr;
+    addr.setFromLocalPort(port);
+    return addr;
+  }
+
+  /**
+   * Similar to makeFromLocalPort(uint16_t port), but accepts the port as
+   * a std::string.
+   *
+   * Raises std::invalid_argument on error.
+   *
+   * @param port The local port number
+   */
+  static SocketAddress makeFromLocalPort(const char* port) {
+    SocketAddress addr;
+    addr.setFromLocalPort(port);
     return addr;
   }
 
diff --git a/folly/folly/String-inl.h b/folly/folly/String-inl.h
--- a/folly/folly/String-inl.h
+++ b/folly/folly/String-inl.h
@@ -289,6 +289,14 @@
 void internalSplit(
     DelimT delim, StringPiece sp, OutputIterator out, bool ignoreEmpty);
 
+/*
+ * Count the number of tokens that would be produced by splitting
+ * the given StringPiece with the given delimiter.
+ * This is used for pre-allocation optimization in split().
+ */
+size_t delimCountTokens(char delim, StringPiece sp, bool ignoreEmpty);
+size_t delimCountTokens(StringPiece delim, StringPiece sp, bool ignoreEmpty);
+
 template <class OutStringT, class Container>
 std::enable_if_t<
     IsSplitSupportedContainer<Container>::value &&
@@ -498,6 +506,35 @@
       StringPiece(input),
       std::back_inserter(out),
       ignoreEmpty);
+}
+
+template <class Delim, class String, class OutputType>
+std::enable_if_t<detail::IsSplitSupportedContainer<OutputType>::value> split(
+    const Delim& delimiter,
+    const String& input,
+    OutputType& out,
+    const SplitOptions& options) {
+  size_t predicted_count = 0;
+  if (options.preallocate()) {
+    // First pass: count the expected number of tokens
+    StringPiece sp(input);
+    auto delim_prepared = detail::prepareDelim(delimiter);
+    predicted_count =
+        detail::delimCountTokens(delim_prepared, sp, options.ignore_empty());
+    grow_capacity_by(out, predicted_count);
+  }
+
+  size_t initial_size = out.size();
+  detail::internalSplit<typename OutputType::value_type>(
+      detail::prepareDelim(delimiter),
+      StringPiece(input),
+      std::back_inserter(out),
+      options.ignore_empty());
+
+  if (options.preallocate()) {
+    [[maybe_unused]] size_t actual_count = out.size() - initial_size;
+    assert(predicted_count == actual_count);
+  }
 }
 
 template <
diff --git a/folly/folly/String.cpp b/folly/folly/String.cpp
--- a/folly/folly/String.cpp
+++ b/folly/folly/String.cpp
@@ -191,10 +191,11 @@
   int bytes_used = stringAppendfImplHelper(
       inline_buffer.data(), inline_buffer.size(), format, args);
   if (bytes_used < 0) {
-    throw std::runtime_error(to<std::string>(
-        "Invalid format string; snprintf returned negative "
-        "with format string: ",
-        format));
+    throw std::runtime_error(
+        to<std::string>(
+            "Invalid format string; snprintf returned negative "
+            "with format string: ",
+            format));
   }
 
   if (static_cast<size_t>(bytes_used) < inline_buffer.size()) {
@@ -366,6 +367,17 @@
     {" ", 0},      {nullptr, 0},
 };
 
+const PrettySuffix kPrettyBitsMetricSuffixes[] = {
+    {"Eb", 1e18L},
+    {"Pb", 1e15L},
+    {"Tb", 1e12L},
+    {"Gb", 1e9L},
+    {"Mb", 1e6L},
+    {"kb", 1e3L},
+    {"b ", 0},
+    {nullptr, 0},
+};
+
 const PrettySuffix* const kPrettySuffixes[PRETTY_NUM_TYPES] = {
     kPrettyTimeSuffixes,
     kPrettyTimeHmsSuffixes,
@@ -376,6 +388,7 @@
     kPrettyUnitsBinarySuffixes,
     kPrettyUnitsBinaryIECSuffixes,
     kPrettySISuffixes,
+    kPrettyBitsMetricSuffixes,
 };
 
 } // namespace
@@ -436,8 +449,9 @@
     }
   }
   if (bestPrefixId == -1) { // No valid suffix rule found
-    throw std::invalid_argument(folly::to<std::string>(
-        "Unable to parse suffix \"", *prettyString, "\""));
+    throw std::invalid_argument(
+        folly::to<std::string>(
+            "Unable to parse suffix \"", *prettyString, "\""));
   }
   prettyString->advance(size_t(longestPrefixLen));
   return suffixes[bestPrefixId].val != 0.
@@ -722,7 +736,7 @@
 
 std::string stripLeftMargin(std::string s) {
   std::vector<StringPiece> pieces;
-  split("\n", s, pieces);
+  split('\n', s, pieces);
   auto piecer = range(pieces);
 
   auto piece = (piecer.end() - 1);
@@ -768,6 +782,58 @@
     const SubstringConversionCode& other) const {
   return this->code == other.code && this->substring == other.substring;
 }
+
+namespace detail {
+
+// Template implementation that both concrete overloads delegate to
+template <class DelimT>
+size_t delimCountTokensImpl(DelimT delim, StringPiece sp, bool ignoreEmpty) {
+  assert(sp.empty() || sp.start() != nullptr);
+
+  const char* s = sp.start();
+  const size_t strSize = sp.size();
+  const size_t dSize = delimSize(delim);
+
+  if (dSize > strSize || dSize == 0) {
+    return (!ignoreEmpty || strSize > 0) ? 1 : 0;
+  }
+
+  size_t tokenCount = 0;
+  size_t tokenStartPos = 0;
+  size_t tokenSize = 0;
+
+  for (size_t i = 0; i <= strSize - dSize; ++i) {
+    if (atDelim(&s[i], delim)) {
+      if (!ignoreEmpty || tokenSize > 0) {
+        ++tokenCount;
+      }
+      tokenStartPos = i + dSize;
+      tokenSize = 0;
+      i += dSize - 1;
+    } else {
+      ++tokenSize;
+    }
+  }
+
+  // Count the final token
+  tokenSize = strSize - tokenStartPos;
+  if (!ignoreEmpty || tokenSize > 0) {
+    ++tokenCount;
+  }
+
+  return tokenCount;
+}
+
+// Concrete overloads that delegate to the template implementation
+size_t delimCountTokens(char delim, StringPiece sp, bool ignoreEmpty) {
+  return delimCountTokensImpl(delim, sp, ignoreEmpty);
+}
+
+size_t delimCountTokens(StringPiece delim, StringPiece sp, bool ignoreEmpty) {
+  return delimCountTokensImpl(delim, sp, ignoreEmpty);
+}
+
+} // namespace detail
 
 } // namespace folly
 
diff --git a/folly/folly/String.h b/folly/folly/String.h
--- a/folly/folly/String.h
+++ b/folly/folly/String.h
@@ -47,6 +47,45 @@
 
 namespace folly {
 
+/// SplitOptions
+///
+/// Options for controlling split() behavior. This class uses a builder pattern
+/// to allow for easy configuration and method chaining.
+class SplitOptions {
+ public:
+  struct Defaults {
+    /// preallocate
+    ///
+    /// If true, split will count the expected number of tokens first
+    /// and pre-allocate container capacity using grow_capacity_by.
+    /// This can improve performance when splitting large strings with
+    /// many tokens, but may add overhead for small strings.
+    static constexpr bool preallocate = false;
+
+    /// ignoreEmpty
+    ///
+    /// If true, adjacent delimiters are treated as one single separator
+    /// (ignoring empty tokens), otherwise empty tokens are generated.
+    static constexpr bool ignore_empty = false;
+  };
+
+  constexpr bool preallocate() const { return preallocate_; }
+  constexpr SplitOptions& preallocate(bool enable) {
+    preallocate_ = enable;
+    return *this;
+  }
+
+  constexpr bool ignore_empty() const { return ignore_empty_; }
+  constexpr SplitOptions& ignore_empty(bool enable) {
+    ignore_empty_ = enable;
+    return *this;
+  }
+
+ private:
+  bool preallocate_ = Defaults::preallocate;
+  bool ignore_empty_ = Defaults::ignore_empty;
+};
+
 /**
  * @overloadbrief C-escape a string.
  *
@@ -348,6 +387,10 @@
   PRETTY_UNITS_BINARY_IEC,
 
   PRETTY_SI,
+
+  PRETTY_BITS_METRIC,
+  PRETTY_BITS = PRETTY_BITS_METRIC,
+
   PRETTY_NUM_TYPES,
 };
 
@@ -372,6 +415,9 @@
  *     PRETTY_UNITS_BINARY_IEC - Ki, Mi, Gi, etc
  *     PRETTY_SI           - full SI metric prefixes from yocto to Yotta
  *                           http://en.wikipedia.org/wiki/Metric_prefix
+ *     PRETTY_BITS_METRIC  - kb, Mb, Gb, etc (goes up by 10^3 = 1000 each time)
+ *                           Useful for network bandwidth (e.g., 1 Gbps)
+ *     PRETTY_BITS         - alias for PRETTY_BITS_METRIC
  *
  */
 std::string prettyPrint(double val, PrettyType, bool addSpace = true);
@@ -525,6 +571,20 @@
     const String& input,
     OutputType& out,
     const bool ignoreEmpty = false);
+
+/**
+ * Split a string into a list of tokens by delimiter with options.
+ *
+ * Same as split() above but with additional options to control behavior.
+ * The SplitOptions allow enabling preallocation which can improve performance
+ * when splitting large strings with many expected tokens.
+ */
+template <class Delim, class String, class OutputType>
+std::enable_if_t<detail::IsSplitSupportedContainer<OutputType>::value> split(
+    const Delim& delimiter,
+    const String& input,
+    OutputType& out,
+    const SplitOptions& options);
 
 /**
  * split, to an output iterator
diff --git a/folly/folly/Subprocess.cpp b/folly/folly/Subprocess.cpp
--- a/folly/folly/Subprocess.cpp
+++ b/folly/folly/Subprocess.cpp
@@ -78,6 +78,8 @@
 
 namespace folly {
 
+using detail::linux_syscall;
+
 namespace detail {
 
 SubprocessFdActionsList::SubprocessFdActionsList(
@@ -318,8 +320,12 @@
 void ProcessReturnCode::enforce(State expected) const {
   State s = state();
   if (s != expected) {
-    throw std::logic_error(to<std::string>(
-        "Bad use of ProcessReturnCode; state is ", s, " expected ", expected));
+    throw std::logic_error(
+        to<std::string>(
+            "Bad use of ProcessReturnCode; state is ",
+            s,
+            " expected ",
+            expected));
   }
 }
 
@@ -705,7 +711,7 @@
   returnCode_ = ProcessReturnCode::makeRunning();
 }
 
-// With -Wclobbered, gcc complains about vfork potentially cloberring the
+// With -Wclobbered, gcc complains about vfork potentially clobbering the
 // childDir variable, even though we only use it on the child side of the
 // vfork.
 
@@ -912,29 +918,51 @@
 #endif
 
   // Change effective/real group/user, if requested
-  if (auto& ptr = args.egid; ptr && 0 != ::setegid(ptr->value)) {
-    if (auto out = ptr->errout) {
+  // Call the raw syscall directly for linux, as glibc set*id() is not
+  // safe after vfork() - Linux is the only kernel where libc set*id is not just
+  // a call to syscall set*id
+  // The risk is that some vfork child is terminated within set*id() with
+  // a lock held, deadlocking the next vfork child
+  auto idval = [](auto id) { return id ? &id->value : nullptr; };
+#if defined(__linux__)
+  constexpr auto k_sys_setresgid = SYS_setresgid;
+  constexpr auto k_sys_setresuid = SYS_setresuid;
+#else
+  // Unused
+  constexpr auto k_sys_setresgid = -1;
+  constexpr auto k_sys_setresuid = -1;
+#endif
+  if (auto p = idval(args.egid); kIsLinux
+          ? p && 0 != linux_syscall(k_sys_setresgid, -1, *p, -1)
+          : p && 0 != ::setegid(*p)) {
+    if (auto out = args.egid->errout) {
       *out = errno;
     } else {
       return errno;
     }
   }
-  if (auto& ptr = args.gid; ptr && 0 != ::setgid(ptr->value)) {
-    if (auto out = ptr->errout) {
+  if (auto p = idval(args.gid); kIsLinux
+          ? p && 0 != linux_syscall(SYS_setgid, *p)
+          : p && 0 != ::setgid(*p)) {
+    if (auto out = args.gid->errout) {
       *out = errno;
     } else {
       return errno;
     }
   }
-  if (auto& ptr = args.euid; ptr && 0 != ::seteuid(ptr->value)) {
-    if (auto out = ptr->errout) {
+  if (auto p = idval(args.euid); kIsLinux
+          ? p && 0 != linux_syscall(k_sys_setresuid, -1, *p, -1)
+          : p && 0 != ::seteuid(*p)) {
+    if (auto out = args.euid->errout) {
       *out = errno;
     } else {
       return errno;
     }
   }
-  if (auto& ptr = args.uid; ptr && 0 != ::setuid(ptr->value)) {
-    if (auto out = ptr->errout) {
+  if (auto p = idval(args.uid); kIsLinux
+          ? p && 0 != linux_syscall(SYS_setuid, *p)
+          : p && 0 != ::setuid(*p)) {
+    if (auto out = args.uid->errout) {
       *out = errno;
     } else {
       return errno;
diff --git a/folly/folly/Subprocess.h b/folly/folly/Subprocess.h
--- a/folly/folly/Subprocess.h
+++ b/folly/folly/Subprocess.h
@@ -476,7 +476,7 @@
      * - If you want to automatically clean up the child process when the
      *   Subprocess is destroyed, use killChildOnDestruction() or
      *   terminateChildOnDestruction()
-     * - If you want to allow the parent process to exit without waiting on thie
+     * - If you want to allow the parent process to exit without waiting on the
      *   child, prefer simply leaking the folly::Subprocess object when the
      *   parent process exits.  You could exit with _exit(), or you could
      *   explicitly leak the Subprocess using std::unique_ptr::release() or
@@ -544,7 +544,7 @@
       int* errout{erroutLifetime_.get()};
     };
 
-    typedef boost::container::flat_map<int, int> FdMap;
+    using FdMap = boost::container::flat_map<int, int>;
     FdMap fdActions_;
     bool closeOtherFds_{false};
     bool usePath_{false};
@@ -659,6 +659,10 @@
    * Poll the child's status.  If the process is still running, return false.
    * Otherwise, return true if the process exited with status 0 (success),
    * or throw CalledProcessError if the process exited with a non-zero status.
+   * Note: this should only be called for processes in the RUNNING state. If
+   * another trigger has caused the process to register as exited, or the
+   * process is yet to start before this is called, this will throw a
+   * std::logic_error.
    */
   bool pollChecked();
 
@@ -862,7 +866,7 @@
       Callback& cb_;
       int fd_;
     };
-    typedef gen::StreamSplitter<StreamSplitterCallback> LineSplitter;
+    using LineSplitter = gen::StreamSplitter<StreamSplitterCallback>;
 
    public:
     explicit ReadLinesCallback(
diff --git a/folly/folly/Synchronized.h b/folly/folly/Synchronized.h
--- a/folly/folly/Synchronized.h
+++ b/folly/folly/Synchronized.h
@@ -998,8 +998,8 @@
  */
 template <class T, class Mutex = SharedMutex>
 struct [[deprecated(
-    "use Synchronized and explicit lock(), wlock(), or rlock() instead")]] ImplicitSynchronized
-    : Synchronized<T, Mutex> {
+    "use Synchronized and explicit lock(), wlock(), or rlock() instead")]]
+ImplicitSynchronized : Synchronized<T, Mutex> {
  private:
   using Base = Synchronized<T, Mutex>;
 
@@ -1756,9 +1756,8 @@
 #define SYNCHRONIZED_VAR(var) FB_CONCATENATE(SYNCHRONIZED_##var##_, __LINE__)
 
 namespace detail {
-struct [[deprecated(
-    "use explicit lock(), wlock(), or rlock() instead")]] SYNCHRONIZED_macro_is_deprecated {
-};
+struct [[deprecated("use explicit lock(), wlock(), or rlock() instead")]]
+SYNCHRONIZED_macro_is_deprecated {};
 } // namespace detail
 
 /**
diff --git a/folly/folly/ThreadLocal.h b/folly/folly/ThreadLocal.h
--- a/folly/folly/ThreadLocal.h
+++ b/folly/folly/ThreadLocal.h
@@ -98,7 +98,7 @@
 
   void reset(T* newPtr = nullptr) { tlp_.reset(newPtr); }
 
-  typedef typename ThreadLocalPtr<T, Tag, AccessMode>::Accessor Accessor;
+  using Accessor = typename ThreadLocalPtr<T, Tag, AccessMode>::Accessor;
   Accessor accessAllThreads() const { return tlp_.accessAllThreads(); }
 
  private:
@@ -145,7 +145,7 @@
 template <class T, class Tag = void, class AccessMode = void>
 class ThreadLocalPtr {
  private:
-  typedef threadlocal_detail::StaticMeta<Tag, AccessMode> StaticMeta;
+  using StaticMeta = threadlocal_detail::StaticMeta<Tag, AccessMode>;
 
   using AccessAllThreadsEnabled = Negation<std::is_same<Tag, void>>;
 
diff --git a/folly/folly/Traits.h b/folly/folly/Traits.h
--- a/folly/folly/Traits.h
+++ b/folly/folly/Traits.h
@@ -72,6 +72,39 @@
 template <std::size_t I>
 using index_constant = std::integral_constant<std::size_t, I>;
 
+namespace detail {
+
+template <typename Int>
+constexpr Int parse_uic(char const* str) noexcept {
+  Int result = 0;
+  while (*str) {
+    auto const c = *str++;
+    if (c >= '0' && c <= '9') {
+      result = result * 10 + (c - '0');
+    }
+  }
+  return result;
+}
+
+} // namespace detail
+
+inline namespace literals {
+inline namespace integral_constant_literals {
+
+/// operator""_uzic
+///
+/// Evaluates {XYZ}_uzic as index_constant<{XYZ}>.
+///
+/// mimic: operator""_uzic, p2725r0
+template <char... Digits>
+constexpr auto operator""_uzic() noexcept {
+  constexpr char digits[] = {Digits..., '\0'};
+  return index_constant<detail::parse_uic<size_t>(digits)>{};
+}
+
+} // namespace integral_constant_literals
+} // namespace literals
+
 /// always_false
 ///
 /// A variable template that is always false but requires template arguments to
@@ -112,7 +145,9 @@
 
 template <typename Void, typename T>
 struct require_sizeof_ {
-  static_assert(always_false<T>, "application of sizeof fails substitution");
+  static_assert(
+      always_false<T>,
+      "application of sizeof fails substitution - most commonly, the type is incomplete");
 };
 template <typename T>
 struct require_sizeof_<decltype(void(sizeof(T))), T> {
@@ -128,6 +163,11 @@
 ///
 /// Equivalent to sizeof, but with a static_assert enforcing that application of
 /// sizeof would not fail substitution.
+///
+/// Application of sizeof fails on the following kinds of types:
+/// * function types.
+/// * incomplete types, including possibly-cv-qualified void
+/// * references to types to which application of sizeof would fail
 template <typename T>
 constexpr std::size_t require_sizeof = detail::require_sizeof_<void, T>::size;
 
@@ -937,6 +977,9 @@
 template <typename T, typename... Ts>
 using IsOneOf = StrictDisjunction<std::is_same<T, Ts>...>;
 
+template <typename T, typename... Ts>
+inline constexpr bool is_one_of_v = IsOneOf<T, Ts...>::value;
+
 /*
  * Complementary type traits for integral comparisons.
  *
@@ -1033,6 +1076,17 @@
 
 namespace folly {
 
+/// is_non_bool_integral_v
+///
+/// A common need.
+template <typename Int>
+inline constexpr bool is_non_bool_integral_v =
+    !std::is_same_v<bool, std::remove_cv_t<Int>> && std::is_integral_v<Int>;
+
+template <typename Int>
+struct is_non_bool_integral //
+    : std::bool_constant<is_non_bool_integral_v<Int>> {};
+
 //  Some compilers have signed __int128 and unsigned __int128 types, and some
 //  libraries with some compilers have traits for those types. It's a mess.
 //  Import things into folly and then fill in whatever is missing.
@@ -1279,23 +1333,18 @@
 inline constexpr type_identity<Out<>> type_list_concat_<Out>;
 
 template <
-    template <typename...>
-    class Out,
-    template <typename...>
-    class In,
+    template <typename...> class Out,
+    template <typename...> class In,
     typename... T>
 inline constexpr auto type_list_concat_<Out, In<T...>> =
     type_identity<Out<T...>>{};
 
 template <
-    template <typename...>
-    class Out,
+    template <typename...> class Out,
     // Allow input lists to come from heterogeneous templates.
-    template <typename...>
-    class InA,
+    template <typename...> class InA,
     typename... A,
-    template <typename...>
-    class InB,
+    template <typename...> class InB,
     typename... B,
     typename... Tail>
 inline constexpr auto type_list_concat_<Out, InA<A...>, InB<B...>, Tail...> =
@@ -1416,14 +1465,11 @@
     type_identity<Out<V...>>{};
 
 template <
-    template <auto...>
-    class Out,
+    template <auto...> class Out,
     // Allow input lists to come from heterogeneous templates.
-    template <auto...>
-    class InA,
+    template <auto...> class InA,
     auto... A,
-    template <auto...>
-    class InB,
+    template <auto...> class InB,
     auto... B,
     typename... Tail>
 inline constexpr auto value_list_concat_<Out, InA<A...>, InB<B...>, Tail...> =
@@ -1444,14 +1490,15 @@
 namespace detail {
 
 template <typename V, typename... T>
-constexpr std::size_t type_pack_find_() {
-  bool eq[] = {std::is_same_v<V, T>..., true};
-  for (size_t i = 0; i < sizeof...(T); ++i) {
-    if (eq[i]) {
-      return i;
-    }
+constexpr bool type_pack_find_a_[sizeof...(T) + 1] = {
+    std::is_same_v<V, T>..., true};
+
+constexpr std::size_t type_pack_find_(bool const* eq) {
+  size_t i = 0;
+  while (!eq[i]) {
+    ++i;
   }
-  return sizeof...(T);
+  return i;
 }
 
 template <typename>
@@ -1459,7 +1506,8 @@
 template <template <typename...> class List, typename... T>
 struct type_list_find_<List<T...>> {
   template <typename V>
-  static inline constexpr std::size_t apply = type_pack_find_<V, T...>();
+  static inline constexpr std::size_t apply =
+      type_pack_find_(type_pack_find_a_<V, T...>);
 };
 
 } // namespace detail
@@ -1470,7 +1518,7 @@
 /// type, or the size of the pack if there is no such element.
 template <typename V, typename... T>
 inline constexpr std::size_t type_pack_find_v =
-    detail::type_pack_find_<V, T...>();
+    detail::type_pack_find_(detail::type_pack_find_a_<V, T...>);
 
 /// type_pack_find_t
 ///
diff --git a/folly/folly/Try.h b/folly/folly/Try.h
--- a/folly/folly/Try.h
+++ b/folly/folly/Try.h
@@ -320,9 +320,6 @@
    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
    *          otherwise, returns `nullptr`.
    */
-  std::exception* tryGetExceptionObject() noexcept {
-    return hasException() ? this->e_.get_exception() : nullptr;
-  }
   std::exception const* tryGetExceptionObject() const noexcept {
     return hasException() ? this->e_.get_exception() : nullptr;
   }
@@ -333,10 +330,6 @@
    *          returns `nullptr`.
    */
   template <class Ex>
-  Ex* tryGetExceptionObject() noexcept {
-    return hasException() ? this->e_.template get_exception<Ex>() : nullptr;
-  }
-  template <class Ex>
   Ex const* tryGetExceptionObject() const noexcept {
     return hasException() ? this->e_.template get_exception<Ex>() : nullptr;
   }
@@ -407,7 +400,7 @@
   /*
    * The value type for the Try
    */
-  typedef void element_type;
+  using element_type = void;
 
   // Construct a Try holding a successful and void result
   Try() noexcept : hasValue_(true) {}
@@ -525,9 +518,6 @@
    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
    *          otherwise, returns `nullptr`.
    */
-  std::exception* tryGetExceptionObject() noexcept {
-    return hasException() ? e_.get_exception() : nullptr;
-  }
   std::exception const* tryGetExceptionObject() const noexcept {
     return hasException() ? e_.get_exception() : nullptr;
   }
@@ -537,10 +527,6 @@
    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
    *          returns `nullptr`.
    */
-  template <class E>
-  E* tryGetExceptionObject() noexcept {
-    return hasException() ? e_.get_exception<E>() : nullptr;
-  }
   template <class E>
   E const* tryGetExceptionObject() const noexcept {
     return hasException() ? e_.get_exception<E>() : nullptr;
diff --git a/folly/folly/Unicode.cpp b/folly/folly/Unicode.cpp
--- a/folly/folly/Unicode.cpp
+++ b/folly/folly/Unicode.cpp
@@ -22,53 +22,53 @@
 
 namespace folly {
 
-namespace {
-
-template <class F>
-void codePointToUtf8Impl(char32_t cp, F&& f) {
+unicode_code_point_utf8 unicode_code_point_to_utf8(char32_t const cp) {
   // Based on description from http://en.wikipedia.org/wiki/UTF-8.
 
+  auto const ret = [](std::initializer_list<char> const ilist) {
+    unicode_code_point_utf8 val{to_narrow(ilist.size()), {}};
+    std::memcpy(val.data, ilist.begin(), ilist.size());
+    return val;
+  };
+
   if (cp <= 0x7f) {
-    f({
+    return ret({
         static_cast<char>(cp),
     });
   } else if (cp <= 0x7FF) {
-    f({
+    return ret({
         static_cast<char>(0xC0 | (cp >> 6)),
         static_cast<char>(0x80 | (0x3f & cp)),
     });
   } else if (cp <= 0xFFFF) {
-    f({
+    return ret({
         static_cast<char>(0xE0 | (cp >> 12)),
         static_cast<char>(0x80 | (0x3f & (cp >> 6))),
         static_cast<char>(0x80 | (0x3f & cp)),
     });
   } else if (cp <= 0x10FFFF) {
-    f({
+    return ret({
         static_cast<char>(0xF0 | (cp >> 18)),
         static_cast<char>(0x80 | (0x3f & (cp >> 12))),
         static_cast<char>(0x80 | (0x3f & (cp >> 6))),
         static_cast<char>(0x80 | (0x3f & cp)),
     });
+  } else {
+    return {0, {}};
   }
 }
 
-} // namespace
+void appendCodePointToUtf8(char32_t const cp, std::string& out) {
+  auto const utf8 = unicode_code_point_to_utf8(cp);
+  out.append(reinterpret_cast<char const*>(utf8.data), utf8.size);
+}
 
-std::string codePointToUtf8(char32_t cp) {
+std::string codePointToUtf8(char32_t const cp) {
   std::string result;
-  codePointToUtf8Impl(cp, [&](std::initializer_list<char> data) {
-    result.assign(data.begin(), data.end());
-  });
+  appendCodePointToUtf8(cp, result);
   return result;
 }
 
-void appendCodePointToUtf8(char32_t cp, std::string& out) {
-  codePointToUtf8Impl(cp, [&](std::initializer_list<char> data) {
-    out.append(data.begin(), data.end());
-  });
-}
-
 char32_t utf8ToCodePoint(
     const unsigned char*& p, const unsigned char* const e, bool skipOnError) {
   // clang-format off
@@ -143,8 +143,9 @@
       if (skipOnError) {
         return skip();
       }
-      throw std::runtime_error(to<std::string>(
-          "folly::utf8ToCodePoint i=", i, " tmp=", (uint32_t)tmp));
+      throw std::runtime_error(
+          to<std::string>(
+              "folly::utf8ToCodePoint i=", i, " tmp=", (uint32_t)tmp));
     }
 
     // gradually fill a 32 bit integer d with non control bits in tmp
diff --git a/folly/folly/Unicode.h b/folly/folly/Unicode.h
--- a/folly/folly/Unicode.h
+++ b/folly/folly/Unicode.h
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include <cstddef>
 #include <cstdint>
 #include <stdexcept>
 #include <string>
@@ -75,13 +76,19 @@
 
 //////////////////////////////////////////////////////////////////////
 
+struct unicode_code_point_utf8 {
+  uint32_t size;
+  uint8_t data[4];
+};
+unicode_code_point_utf8 unicode_code_point_to_utf8(char32_t cp);
+
 /*
  * Encode a single Unicode code point into a UTF-8 byte sequence.
  *
  * Result is undefined if `cp' is an invalid code point.
  */
-std::string codePointToUtf8(char32_t cp);
 void appendCodePointToUtf8(char32_t cp, std::string& out);
+std::string codePointToUtf8(char32_t cp);
 
 /*
  * Decode a single Unicode code point from UTF-8 byte sequence.
diff --git a/folly/folly/Uri-inl.h b/folly/folly/Uri-inl.h
--- a/folly/folly/Uri-inl.h
+++ b/folly/folly/Uri-inl.h
@@ -58,23 +58,23 @@
   if (hasAuthority_) {
     toAppend(scheme_, "://", &str);
     if (!password_.empty()) {
-      toAppend(username_, ":", password_, "@", &str);
+      toAppend(username_, ':', password_, '@', &str);
     } else if (!username_.empty()) {
-      toAppend(username_, "@", &str);
+      toAppend(username_, '@', &str);
     }
     toAppend(host_, &str);
     if (port_ != 0) {
-      toAppend(":", port_, &str);
+      toAppend(':', port_, &str);
     }
   } else {
-    toAppend(scheme_, ":", &str);
+    toAppend(scheme_, ':', &str);
   }
   toAppend(path_, &str);
   if (!query_.empty()) {
-    toAppend("?", query_, &str);
+    toAppend('?', query_, &str);
   }
   if (!fragment_.empty()) {
-    toAppend("#", fragment_, &str);
+    toAppend('#', fragment_, &str);
   }
   return str;
 }
diff --git a/folly/folly/Uri.cpp b/folly/folly/Uri.cpp
--- a/folly/folly/Uri.cpp
+++ b/folly/folly/Uri.cpp
@@ -32,7 +32,7 @@
 
 } // namespace
 
-// private default contructor
+// private default constructor
 Uri::Uri() : hasAuthority_(false), port_(0) {}
 
 // public string constructor
diff --git a/folly/folly/Utility.h b/folly/folly/Utility.h
--- a/folly/folly/Utility.h
+++ b/folly/folly/Utility.h
@@ -355,26 +355,30 @@
   }
 };
 
+template <typename T>
+constexpr bool is_inheritable_v_ = //
+    (std::is_class_v<T> || std::is_union_v<T>) &&
+    !(std::is_abstract_v<T> || std::is_final_v<T>);
+
 template <bool>
 struct inheritable_;
 template <>
 struct inheritable_<false> {
   template <typename T>
-  using apply = inheritable_inherit_<T>;
+  using apply = inheritable_contain_<T>;
 };
 template <>
 struct inheritable_<true> {
   template <typename T>
-  using apply = inheritable_contain_<T>;
+  using apply = inheritable_inherit_<T>;
 };
 
 //  inheritable
 //
 //  A class wrapping an arbitrary type T which is always inheritable, and which
 //  enables empty-base-optimization when possible.
-template <typename T>
-using inheritable =
-    typename inheritable_<std::is_final<T>::value>::template apply<T>;
+template <typename T, bool C = is_inheritable_v_<T>>
+using inheritable = typename inheritable_<C>::template apply<T>;
 
 } // namespace detail
 
@@ -435,7 +439,7 @@
 /// May be invoked with any arguments. Returns void.
 struct variadic_noop_fn {
   template <typename... A>
-  constexpr void operator()(A&&...) const noexcept {}
+  constexpr void operator()(A&&... /*unused*/) const noexcept {}
 };
 inline constexpr variadic_noop_fn variadic_noop;
 
@@ -450,7 +454,7 @@
   using value_type = decltype(Value);
   static inline constexpr value_type value = Value;
   template <typename... A>
-  constexpr value_type operator()(A&&...) const noexcept {
+  constexpr value_type operator()(A&&... /*unused*/) const noexcept {
     return value;
   }
 };
@@ -497,6 +501,8 @@
   FOLLY_MSVC_DISABLE_WARNING(4701)
   // Potentially uninitialized local pointer variable 'uninit' used
   FOLLY_MSVC_DISABLE_WARNING(4703)
+  // Using uninitialized memory `uninit`
+  FOLLY_MSVC_DISABLE_WARNING(6001)
   FOLLY_GNU_DISABLE_WARNING("-Wuninitialized")
   // Clang doesn't implement -Wmaybe-uninitialized and warns about it
   FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
@@ -549,7 +555,7 @@
     // be implementation-defined behavior and that is typically to be avoided;
     // the following code optimized into the same thing, though
     constexpr auto m = static_cast<T>(std::numeric_limits<S>::max());
-    return m < t ? -static_cast<S>(~t) + S{-1} : static_cast<S>(t);
+    return static_cast<S>(m < t ? -static_cast<S>(~t) + S{-1} : t);
   }
 };
 inline constexpr to_signed_fn to_signed{};
@@ -751,6 +757,8 @@
  private:
   static_assert(std::is_same<F, decay_t<F>>::value, "mismatch");
 
+  using base = inheritable<F>;
+
   template <typename R>
   using result_t = detected_t<invocable_to_detect, R>;
   template <typename R>
@@ -771,6 +779,11 @@
   static_assert(std::is_same<TMR, result_t<FMR>>::value, "mismatch");
   static_assert(std::is_same<TCR, result_t<FCR>>::value, "mismatch");
 
+  using BML = base&;
+  using BCL = base const&;
+  using BMR = base&&;
+  using BCR = base const&&;
+
  public:
   template <typename G, std::enable_if_t<constructible_v<G&&>, int> = 0>
   FOLLY_ERASE explicit constexpr invocable_to_convertible(G&& g) noexcept(
@@ -779,19 +792,19 @@
 
   template <typename..., typename R = FML, if_invocable_as_v<R> = 0>
   FOLLY_ERASE constexpr operator TML() & noexcept(nx_v<R>) {
-    return static_cast<FML>(*this)();
+    return static_cast<FML>(static_cast<BML>(*this))();
   }
   template <typename..., typename R = FCL, if_invocable_as_v<R> = 0>
   FOLLY_ERASE constexpr operator TCL() const& noexcept(nx_v<R>) {
-    return static_cast<FCL>(*this)();
+    return static_cast<FCL>(static_cast<BCL>(*this))();
   }
   template <typename..., typename R = FMR, if_invocable_as_v<R> = 0>
   FOLLY_ERASE constexpr operator TMR() && noexcept(nx_v<R>) {
-    return static_cast<FMR>(*this)();
+    return static_cast<FMR>(static_cast<BMR>(*this))();
   }
   template <typename..., typename R = FCR, if_invocable_as_v<R> = 0>
   FOLLY_ERASE constexpr operator TCR() const&& noexcept(nx_v<R>) {
-    return static_cast<FCR>(*this)();
+    return static_cast<FCR>(static_cast<BCR>(*this))();
   }
 };
 } // namespace detail
@@ -857,6 +870,59 @@
   }
 };
 inline constexpr invocable_to_fn invocable_to{};
+
+/// object_from_member
+/// object_from_member_fn
+///
+/// Returns the object containing the given field.
+///
+/// Similar to container_of (linux kernel).
+///
+/// Example:
+///
+///   using obj_t = std::pair<int, float>;
+///   obj_t obj = {1, 3.0};
+///   assert(&obj == object_from_member(obj_t::second, &obj.second);
+struct object_from_member_fn {
+ private:
+  template <typename M, typename O>
+  using ptr_t = M O::*;
+
+  template <typename M, typename O>
+  static std::ptrdiff_t off(ptr_t<M, O> const p) noexcept {
+    O* o = nullptr;
+    M* m = &(o->*p);
+    return reinterpret_cast<char*>(m) - reinterpret_cast<char*>(o);
+  }
+
+ public:
+  template <typename M, typename O>
+  O* operator()(ptr_t<M, O> const p, M* m) const noexcept {
+    return reinterpret_cast<O*>(reinterpret_cast<char*>(m) - off(p));
+  }
+  template <typename M, typename O>
+  O const* operator()(ptr_t<M, O> const p, M const* m) const noexcept {
+    return reinterpret_cast<O*>(reinterpret_cast<char const*>(m) - off(p));
+  }
+
+  template <typename M, typename O>
+  O& operator()(ptr_t<M, O> const p, M& m) const noexcept {
+    return *operator()(p, &m);
+  }
+  template <typename M, typename O>
+  O const& operator()(ptr_t<M, O> const p, M const& m) const noexcept {
+    return *operator()(p, &m);
+  }
+  template <typename M, typename O>
+  O&& operator()(ptr_t<M, O> const p, M&& m) const noexcept {
+    return static_cast<O&&>(*operator()(p, &m));
+  }
+  template <typename M, typename O>
+  O const&& operator()(ptr_t<M, O> const p, M const& m) const noexcept {
+    return static_cast<O const&&>(*operator()(p, &m));
+  }
+};
+inline constexpr object_from_member_fn object_from_member{};
 
 #define FOLLY_DETAIL_FORWARD_BODY(...)                     \
   noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { \
diff --git a/folly/folly/VirtualExecutor.h b/folly/folly/VirtualExecutor.h
--- a/folly/folly/VirtualExecutor.h
+++ b/folly/folly/VirtualExecutor.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/executors/VirtualExecutor.h>
+#include <folly/executors/VirtualExecutor.h> // @shim
diff --git a/folly/folly/algorithm/simd/Contains.h b/folly/folly/algorithm/simd/Contains.h
--- a/folly/folly/algorithm/simd/Contains.h
+++ b/folly/folly/algorithm/simd/Contains.h
@@ -55,7 +55,7 @@
 }
 
 // All the requirements to call contains(haystack, needle);
-//  * both are simd friendly (contigious range, primitive types)
+//  * both are simd friendly (contiguous range, primitive types)
 //  * integrals only
 //  * needle can be converted to the value_type of haystack and
 //    the result of equality comparison will be the same.
diff --git a/folly/folly/algorithm/simd/Ignore.h b/folly/folly/algorithm/simd/Ignore.h
--- a/folly/folly/algorithm/simd/Ignore.h
+++ b/folly/folly/algorithm/simd/Ignore.h
@@ -37,7 +37,7 @@
  * register: [true, true, false, false, false, false, false, true]
  * indexes   [0,    1,    2,     3,     4,     5,     6,     7   ]
  *
- * ignore_extema{.first = 1, .last = 2}
+ * ignore_extrema{.first = 1, .last = 2}
  * means that elements with indexes 0, 6, and 7 will be ignored
  * (w/e that means for an operation)
  */
diff --git a/folly/folly/algorithm/simd/detail/SimdAnyOf.h b/folly/folly/algorithm/simd/detail/SimdAnyOf.h
--- a/folly/folly/algorithm/simd/detail/SimdAnyOf.h
+++ b/folly/folly/algorithm/simd/detail/SimdAnyOf.h
@@ -64,7 +64,7 @@
  * simdAnyOf<Platform, unrolling = 4>(f, l, p);
  *
  * Like std::any_of but with vectorized predicates.
- * Predicate shoud accept Platform::reg_t and return Platform::logical_t.
+ * Predicate should accept Platform::reg_t and return Platform::logical_t.
  *
  * By default is unrolled 4 ways but for expensive predicates you might want to
  * use an unroll factor of 1.
diff --git a/folly/folly/algorithm/simd/detail/SimdForEach.h b/folly/folly/algorithm/simd/detail/SimdForEach.h
--- a/folly/folly/algorithm/simd/detail/SimdForEach.h
+++ b/folly/folly/algorithm/simd/detail/SimdForEach.h
@@ -79,7 +79,7 @@
 /**
  * SimdForEachMainLoop
  *
- * Implementaiton detail of simdForEach
+ * Implementation detail of simdForEach
  *
  * Regardless of how you chose to handle tails, the middle will be the same.
  * The operator() returns true if the delegate returned to break.
@@ -95,8 +95,9 @@
   FOLLY_ALWAYS_INLINE bool operator()(
       int cardinal, T*& f, T* l, Delegate& delegate, index_constant<1>) const {
     while (f != l) {
-      if (delegate.step(f, ignore_none{}, index_constant<0>{}))
+      if (delegate.step(f, ignore_none{}, index_constant<0>{})) {
         return true;
+      }
       f += cardinal;
     }
 
@@ -113,8 +114,9 @@
 
     template <std::size_t i>
     FOLLY_ALWAYS_INLINE bool operator()(index_constant<i> unrollI) {
-      if (f == l)
+      if (f == l) {
         return true;
+      }
 
       shouldBreak = delegate.step(f, ignore_none{}, unrollI);
       f += cardinal;
diff --git a/folly/folly/algorithm/simd/detail/SimdPlatform.h b/folly/folly/algorithm/simd/detail/SimdPlatform.h
--- a/folly/folly/algorithm/simd/detail/SimdPlatform.h
+++ b/folly/folly/algorithm/simd/detail/SimdPlatform.h
@@ -54,7 +54,7 @@
 template <typename Platform>
 struct SimdPlatformCommon {
   /**
-   * sclar_t - type of scalar we operate on (uint8_t, uint16_t etc)
+   * scalar_t - type of scalar we operate on (uint8_t, uint16_t etc)
    * reg_t - type of a simd register (__m128i)
    * logical_t - type of a simd logical register (matches reg_t so far)
    **/
@@ -281,7 +281,7 @@
   FOLLY_ERASE
   static bool any(logical_t log) { return movemask<scalar_t>(log).first; }
 
-#if 0 // disabled untill we have a test where this is relevant
+#if 0 // disabled until we have a test where this is relevant
   FOLLY_ERASE
   static bool all(logical_t log) {
     auto [bits, bitsPerElement] = movemask<scalar_t>(log);
@@ -360,7 +360,7 @@
   FOLLY_ERASE
   static bool any(logical_t log) { return simd::movemask<scalar_t>(log).first; }
 
-#if 0 // disabled untill we have a test where this is relevant
+#if 0 // disabled until we have a test where this is relevant
   FOLLY_ERASE
   static bool all(logical_t log) {
     auto [bits, bitsPerElement] = movemask<scalar_t>(log);
@@ -480,7 +480,7 @@
     return vgetq_lane_u64(u64, 0);
   }
 
-#if 0 // disabled untill we have a test where this is relevant
+#if 0 // disabled until we have a test where this is relevant
   FOLLY_ERASE
   static bool all(logical_t log) {
     // Not quite what they did in .Net runtime, but
diff --git a/folly/folly/algorithm/simd/detail/UnrollUtils.h b/folly/folly/algorithm/simd/detail/UnrollUtils.h
--- a/folly/folly/algorithm/simd/detail/UnrollUtils.h
+++ b/folly/folly/algorithm/simd/detail/UnrollUtils.h
@@ -42,7 +42,7 @@
    * return an array of results.
    */
   template <typename T, std::size_t N, typename Op>
-  FOLLY_NODISCARD FOLLY_ALWAYS_INLINE static constexpr auto arrayMap(
+  [[nodiscard]] FOLLY_ALWAYS_INLINE static constexpr auto arrayMap(
       const std::array<T, N>& x, Op op) {
     return arrayMapImpl(x, op, std::make_index_sequence<N>());
   }
@@ -56,7 +56,7 @@
    * (a + b) + (c + d)
    */
   template <typename T, std::size_t N, typename Op>
-  FOLLY_NODISCARD FOLLY_ALWAYS_INLINE static constexpr T arrayReduce(
+  [[nodiscard]] FOLLY_ALWAYS_INLINE static constexpr T arrayReduce(
       const std::array<T, N>& x, Op op) {
     return arrayReduceImpl<0, N>(x, op);
   }
@@ -64,11 +64,10 @@
   /**
    * unrollUntil<N>(op)
    *
-   *  Do operation N times or until it returns true to break.
-   *  Op accepts integral_constant<i> so it can keep track of a step begin
-   * executed.
+   * Do operation N times or until it returns true to break.
+   * Op accepts index_constant so it can keep track of a step begin executed.
    *
-   *  Returns wether true if it was interrupted (you can know if the op breaked)
+   * Returns whether true if it was interrupted (you can know if the op broke)
    */
   template <std::size_t N, typename Op>
   FOLLY_ALWAYS_INLINE static constexpr bool unrollUntil(Op op) {
@@ -78,7 +77,7 @@
  private:
   template <typename T, std::size_t N, typename Op, std::size_t... i>
   FOLLY_ALWAYS_INLINE static constexpr auto arrayMapImpl(
-      const std::array<T, N>& x, Op op, std::index_sequence<i...>) {
+      const std::array<T, N>& x, Op op, std::index_sequence<i...> /*unused*/) {
     using U = decltype(op(std::declval<const T&>()));
 
     FOLLY_PUSH_WARNING
@@ -98,7 +97,7 @@
       std::size_t N,
       typename Op>
   FOLLY_ALWAYS_INLINE static constexpr std::enable_if_t<l - f == 1, T>
-  arrayReduceImpl(std::array<T, N> const& x, Op) {
+  arrayReduceImpl(std::array<T, N> const& x, Op /*unused*/) {
     return x[f];
   }
 
@@ -118,7 +117,7 @@
 
   template <typename Op, std::size_t... i>
   FOLLY_ALWAYS_INLINE static constexpr bool unrollUntilImpl(
-      Op op, std::index_sequence<i...>) {
+      Op op, std::index_sequence<i...> /*unused*/) {
     return (... || op(index_constant<i>{}));
   }
 };
diff --git a/folly/folly/base64.h b/folly/folly/base64.h
--- a/folly/folly/base64.h
+++ b/folly/folly/base64.h
@@ -64,6 +64,7 @@
 inline auto base64Decode(std::string_view s) -> std::string;
 inline auto base64URLEncode(std::string_view s) -> std::string;
 inline auto base64URLDecode(std::string_view s) -> std::string;
+inline auto base64PHPStrictDecode(std::string_view s) -> std::string;
 
 // Low level API.
 //
@@ -100,6 +101,11 @@
     const char* f, const char* l) noexcept;
 constexpr std::size_t base64URLDecodedSize(std::string_view s) noexcept;
 
+constexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(
+    const char* f, const char* l) noexcept;
+constexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(
+    std::string_view s) noexcept;
+
 struct base64_decode_result {
   bool is_success;
   char* o;
@@ -125,6 +131,11 @@
 inline base64_decode_result base64URLDecodeRuntime(
     std::string_view s, char* o) noexcept;
 
+inline constexpr base64_decode_result base64PHPStrictDecode(
+    const char* f, const char* l, char* o) noexcept;
+inline constexpr base64_decode_result base64PHPStrictDecode(
+    std::string_view s, char* o) noexcept;
+
 // -----------------------------------------------------------------
 // implementation
 
@@ -193,6 +204,17 @@
   return folly::base64URLDecodedSize(s.data(), s.data() + s.size());
 }
 
+constexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(
+    const char* f, const char* l) noexcept {
+  return detail::base64_detail::base64PHPStrictDecodeRequiredOutputSize(f, l);
+}
+
+constexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(
+    std::string_view s) noexcept {
+  return folly::base64PHPStrictDecodeRequiredOutputSize(
+      s.data(), s.data() + s.size());
+}
+
 inline constexpr base64_decode_result base64Decode(
     const char* f, const char* l, char* o) noexcept {
   auto detailResult = detail::base64_detail::base64Decode(f, l, o);
@@ -237,6 +259,17 @@
   return folly::base64URLDecodeRuntime(s.data(), s.data() + s.size(), o);
 }
 
+inline constexpr base64_decode_result base64PHPStrictDecode(
+    const char* f, const char* l, char* o) noexcept {
+  auto detailResult = detail::base64_detail::base64PHPStrictDecode(f, l, o);
+  return {detailResult.isSuccess, detailResult.o};
+}
+
+inline constexpr base64_decode_result base64PHPStrictDecode(
+    std::string_view s, char* o) noexcept {
+  return folly::base64PHPStrictDecode(s.data(), s.data() + s.size(), o);
+}
+
 // NOTE: for resizeWithoutInitialization we don't need to declare the macros,
 //       since we are using char which is already included by default.
 inline std::string base64Decode(std::string_view s) {
@@ -261,15 +294,24 @@
   return res;
 }
 
-inline bool isBase64URL(std::string_view s) {
+inline std::string base64PHPStrictDecode(std::string_view s) {
   std::string res;
-  std::size_t resSize = folly::base64URLDecodedSize(s);
-  folly::resizeWithoutInitialization(res, resSize);
+  folly::resizeWithoutInitialization(
+      res, base64PHPStrictDecodeRequiredOutputSize(s));
 
-  if (!folly::base64URLDecodeRuntime(s, res.data()).is_success) {
-    return false;
+  if (s.empty()) {
+    return res;
   }
-  return true;
+
+  base64_decode_result decoded = folly::base64PHPStrictDecode(s, res.data());
+
+  if (!decoded.is_success) {
+    folly::throw_exception<base64_decode_error>("Base64PHP Decoding failed");
+  }
+
+  res.resize(decoded.o - res.data());
+  res.shrink_to_fit();
+  return res;
 }
 
 } // namespace folly
diff --git a/folly/folly/channels/Channel-inl.h b/folly/folly/channels/Channel-inl.h
--- a/folly/folly/channels/Channel-inl.h
+++ b/folly/folly/channels/Channel-inl.h
@@ -84,7 +84,8 @@
     });
   }
 
-  bool await_suspend(folly::coro::coroutine_handle<> awaitingCoroutine) {
+  bool await_suspend(
+      folly::coro::coroutine_handle<> awaitingCoroutine) noexcept {
     return state_.withWLock([&](State& state) {
       if (state.cancelled || !state.receiver ||
           !receiverWait(*state.receiver, this)) {
@@ -105,12 +106,12 @@
     return std::move(result.value());
   }
 
-  // FIXME: The default implementation of `co_await_result` will convert the
-  // `getResult()` branch below that returns an empty `Try` into an error
-  // state.  That's because the normal `folly::coro` contract does not allow
-  // producing an empty `Try`.  Therefore, any future `await_resume_result()`
-  // implementation should investigate the intended semantics of this
-  // `getResult()` logic, and provide better handling:
+  // FIXME: The default implementation of `value_or_error_or_stopped` will
+  // convert the `getResult()` branch below that returns an empty `Try` into an
+  // error state.  That's because the normal `folly::coro` contract does not
+  // allow producing an empty `Try`.  Therefore, any future
+  // `await_resume_result()` implementation should investigate the intended
+  // semantics of this `getResult()` logic, and provide better handling:
   //
   //   if (!state.receiver) {
   //     return Try<TValue>();
diff --git a/folly/folly/channels/ChannelProcessor-inl.h b/folly/folly/channels/ChannelProcessor-inl.h
--- a/folly/folly/channels/ChannelProcessor-inl.h
+++ b/folly/folly/channels/ChannelProcessor-inl.h
@@ -37,8 +37,9 @@
       std::shared_ptr<folly::channels::RateLimiter> rateLimiter,
       MergeChannel<KeyType, Unit> mergeChannel,
       Receiver<MergeChannelEvent<KeyType, Unit>> mergeChannelReceiver)
-      : implState_(make_intrusive<ImplState>(
-            std::move(executors), std::move(rateLimiter))),
+      : implState_(
+            make_intrusive<ImplState>(
+                std::move(executors), std::move(rateLimiter))),
         channels_(std::move(mergeChannel)),
         handle_(consumeChannelWithCallback(
             std::move(mergeChannelReceiver),
@@ -118,8 +119,7 @@
 
   template <
       typename Function,
-      typename ReturnType =
-          typename std::invoke_result_t<Function>::StorageType>
+      typename ReturnType = typename invoke_result_t<Function>::StorageType>
   static folly::coro::Task<ReturnType> catchNonCoroException(Function func) {
     auto result = folly::makeTryWith(std::move(func));
     if (result.hasException()) {
diff --git a/folly/folly/channels/ConsumeChannel-inl.h b/folly/folly/channels/ConsumeChannel-inl.h
--- a/folly/folly/channels/ConsumeChannel-inl.h
+++ b/folly/folly/channels/ConsumeChannel-inl.h
@@ -146,8 +146,9 @@
     CHECK_EQ(getReceiverState(), ChannelState::CancellationTriggered);
     receiver_ = nullptr;
     if (fromHandleDestruction) {
-      co_await callCallback(Try<TValue>(
-          folly::make_exception_wrapper<folly::OperationCancelled>()));
+      co_await callCallback(
+          Try<TValue>(
+              folly::make_exception_wrapper<folly::OperationCancelled>()));
     }
     maybeDelete();
   }
diff --git a/folly/folly/channels/Merge-inl.h b/folly/folly/channels/Merge-inl.h
--- a/folly/folly/channels/Merge-inl.h
+++ b/folly/folly/channels/Merge-inl.h
@@ -86,8 +86,9 @@
         CHECK(unbufferedInputReceiver != nullptr)
             << "The bridge in the input receiver is null.";
         CHECK(buffers
-                  .insert(std::make_pair(
-                      unbufferedInputReceiver.get(), std::move(buffer)))
+                  .insert(
+                      std::make_pair(
+                          unbufferedInputReceiver.get(), std::move(buffer)))
                   .second);
         receivers_.insert(unbufferedInputReceiver.release());
       }
diff --git a/folly/folly/channels/MergeChannel-inl.h b/folly/folly/channels/MergeChannel-inl.h
--- a/folly/folly/channels/MergeChannel-inl.h
+++ b/folly/folly/channels/MergeChannel-inl.h
@@ -196,16 +196,18 @@
       auto keyToRemove = existingReceiverIt->first;
       state->receivers[existingReceiverIt->second] = nullptr;
       state->receiversByKey.erase(existingReceiverIt);
-      state->sender->senderPush(MergeChannelEvent<KeyType, ValueType>{
-          keyToRemove, MergeChannelReceiverRemoved{}});
+      state->sender->senderPush(
+          MergeChannelEvent<KeyType, ValueType>{
+              keyToRemove, MergeChannelReceiverRemoved{}});
     }
     auto [it, _] = state->receiversByKey.insert(
         std::make_pair(key, unbufferedReceiver.get()));
     auto* receiverPtr = unbufferedReceiver.get();
     state->receivers.insert(
         std::make_pair(unbufferedReceiver.release(), &it->first));
-    state->sender->senderPush(MergeChannelEvent<KeyType, ValueType>{
-        key, MergeChannelReceiverAdded{}});
+    state->sender->senderPush(
+        MergeChannelEvent<KeyType, ValueType>{
+            key, MergeChannelReceiverAdded{}});
     processAllAvailableValues(state, receiverPtr, std::move(buffer));
   }
 
@@ -228,8 +230,9 @@
     auto keyToRemove = receiverIt->first;
     state->receivers[receiverIt->second] = nullptr;
     state->receiversByKey.erase(receiverIt);
-    state->sender->senderPush(MergeChannelEvent<KeyType, ValueType>{
-        keyToRemove, MergeChannelReceiverRemoved{}});
+    state->sender->senderPush(
+        MergeChannelEvent<KeyType, ValueType>{
+            keyToRemove, MergeChannelReceiverRemoved{}});
   }
 
   folly::F14FastSet<KeyType> getReceiverKeys() {
@@ -349,8 +352,9 @@
       if (inputResult.hasValue()) {
         // We have received a normal value from an input receiver. Write it to
         // the output receiver.
-        state->sender->senderPush(MergeChannelEvent<KeyType, ValueType>{
-            *key, std::move(inputResult.value())});
+        state->sender->senderPush(
+            MergeChannelEvent<KeyType, ValueType>{
+                *key, std::move(inputResult.value())});
       } else {
         // The input receiver was closed.
         return inputResult.hasException()
@@ -374,12 +378,13 @@
       auto keyToRemove = *key;
       CHECK_EQ(state->receiversByKey.erase(keyToRemove), 1);
       if (state->getSenderState() == ChannelState::Active) {
-        state->sender->senderPush(MergeChannelEvent<KeyType, ValueType>{
-            keyToRemove,
-            MergeChannelReceiverClosed{
-                closeResult.exception.has_value()
-                    ? std::move(closeResult.exception.value())
-                    : exception_wrapper()}});
+        state->sender->senderPush(
+            MergeChannelEvent<KeyType, ValueType>{
+                keyToRemove,
+                MergeChannelReceiverClosed{
+                    closeResult.exception.has_value()
+                        ? std::move(closeResult.exception.value())
+                        : exception_wrapper()}});
       }
     }
     state->receivers.erase(receiver);
@@ -414,8 +419,9 @@
     if (state->getSenderState() == ChannelState::Active) {
       for (auto [key, receiver] : state->receiversByKey) {
         state->receivers[receiver] = nullptr;
-        state->sender->senderPush(MergeChannelEvent<KeyType, ValueType>{
-            key, MergeChannelReceiverRemoved{}});
+        state->sender->senderPush(
+            MergeChannelEvent<KeyType, ValueType>{
+                key, MergeChannelReceiverRemoved{}});
       }
       if (closeResult.exception.has_value()) {
         state->sender->senderClose(std::move(closeResult.exception.value()));
diff --git a/folly/folly/channels/MultiplexChannel-inl.h b/folly/folly/channels/MultiplexChannel-inl.h
--- a/folly/folly/channels/MultiplexChannel-inl.h
+++ b/folly/folly/channels/MultiplexChannel-inl.h
@@ -185,7 +185,7 @@
   void start(Receiver<InputValueType> inputReceiver) {
     executeWithMutexWhenReady(
         [this, inputReceiver = std::move(inputReceiver)]() mutable
-        -> folly::coro::Task<void> {
+            -> folly::coro::Task<void> {
           co_await processStart(std::move(inputReceiver));
         });
   }
@@ -225,7 +225,7 @@
   void destroyHandle(CloseResult closeResult) {
     executeWithMutexWhenReady(
         [this, closeResult = std::move(closeResult)]() mutable
-        -> folly::coro::Task<void> {
+            -> folly::coro::Task<void> {
           co_await processHandleDestroyed(std::move(closeResult));
         });
   }
@@ -387,8 +387,9 @@
     for (auto it = subscriptions_.begin(); it != subscriptions_.end();) {
       auto& sender = std::get<FanoutSender<OutputValueType>>(it->second);
       if (!sender.anySubscribers()) {
-        clearedSubscriptions.push_back(std::make_pair(
-            it->first, std::move(std::get<KeyContextType>(it->second))));
+        clearedSubscriptions.push_back(
+            std::make_pair(
+                it->first, std::move(std::get<KeyContextType>(it->second))));
         it = subscriptions_.erase(it);
         subscriptionsToRemove++;
       } else {
diff --git a/folly/folly/channels/ProxyChannel.h b/folly/folly/channels/ProxyChannel.h
--- a/folly/folly/channels/ProxyChannel.h
+++ b/folly/folly/channels/ProxyChannel.h
@@ -54,6 +54,8 @@
 
  public:
   explicit ProxyChannel(detail::ProxyChannelProcessor<ValueType>* processor);
+  ProxyChannel(const ProxyChannel&) = delete;
+  ProxyChannel& operator=(const ProxyChannel&) = delete;
   ProxyChannel(ProxyChannel&& other) noexcept;
   ProxyChannel& operator=(ProxyChannel&& other) noexcept;
   ~ProxyChannel();
diff --git a/folly/folly/channels/Transform-inl.h b/folly/folly/channels/Transform-inl.h
--- a/folly/folly/channels/Transform-inl.h
+++ b/folly/folly/channels/Transform-inl.h
@@ -430,7 +430,7 @@
         // user's initialization function and resume the resumableTransform.
         auto* reinitializeEx =
             closeResult.exception
-                ->get_exception<ReinitializeException<InitializeArg>>();
+                ->get_mutable_exception<ReinitializeException<InitializeArg>>();
         co_await initializeImpl(std::move(reinitializeEx->initializeArg));
         co_return;
       }
diff --git a/folly/folly/channels/detail/PointerVariant.h b/folly/folly/channels/detail/PointerVariant.h
--- a/folly/folly/channels/detail/PointerVariant.h
+++ b/folly/folly/channels/detail/PointerVariant.h
@@ -84,13 +84,14 @@
  private:
   void ensureCorrectType(bool secondType) const {
     if (secondType != !!(storage_ & kTypeMask)) {
-      throw std::runtime_error(fmt::format(
-          "Incorrect type specified. Given: {}, Stored: {}",
-          secondType ? folly::demangle(typeid(SecondType).name())
-                     : folly::demangle(typeid(FirstType).name()),
-          storage_ & kTypeMask
-              ? folly::demangle(typeid(SecondType).name())
-              : folly::demangle(typeid(FirstType).name())));
+      throw std::runtime_error(
+          fmt::format(
+              "Incorrect type specified. Given: {}, Stored: {}",
+              secondType ? folly::demangle(typeid(SecondType).name())
+                         : folly::demangle(typeid(FirstType).name()),
+              storage_ & kTypeMask
+                  ? folly::demangle(typeid(SecondType).name())
+                  : folly::demangle(typeid(FirstType).name())));
     }
   }
 
diff --git a/folly/folly/channels/detail/Utility.h b/folly/folly/channels/detail/Utility.h
--- a/folly/folly/channels/detail/Utility.h
+++ b/folly/folly/channels/detail/Utility.h
@@ -172,7 +172,7 @@
   void consume(ChannelBridgeBase*) override {
     cancelSource_.requestCancellation();
     executor_->add([this]() {
-      CHECK(!callbackToFire_.second.isReady());
+      CHECK(!callbackToFire_.first.isFulfilled());
       callbackToFire_.first.setValue(CallbackToFire::Consume);
     });
   }
@@ -184,7 +184,7 @@
   void canceled(ChannelBridgeBase*) override {
     cancelSource_.requestCancellation();
     executor_->add([this]() {
-      CHECK(!callbackToFire_.second.isReady());
+      CHECK(!callbackToFire_.first.isFulfilled());
       callbackToFire_.first.setValue(CallbackToFire::Canceled);
     });
   }
diff --git a/folly/folly/chrono/Conv.h b/folly/folly/chrono/Conv.h
--- a/folly/folly/chrono/Conv.h
+++ b/folly/folly/chrono/Conv.h
@@ -71,7 +71,7 @@
  * fits in the range of numbers representable by time_t.
  *
  * This is similar to the normal folly::tryTo() behavior when converting
- * arthmetic types to an integer type, except that it does not complain about
+ * arithmetic types to an integer type, except that it does not complain about
  * floating point conversions losing precision.
  */
 template <typename Src>
@@ -87,7 +87,7 @@
     // 18446744073709551615 <- time_t max (when time_t is int64_t)
     // 18446744073709551616 <- next representable float or double.
     // The floating point value that gets chosen depends on the floating point
-    // implementation. IEEE arthimetic rounds to nearest.
+    // implementation. IEEE arithmetic rounds to nearest.
     static_assert(
         std::numeric_limits<Src>::round_style == std::round_to_nearest,
         "This function is only implemented for IEEE round to nearest. Please update it if you need other round styles.");
@@ -523,12 +523,12 @@
     // Perform the division as an integer, and check that the result fits in
     // the output integer type
     auto outputValue = (seconds / Tgt::period::num);
-    auto expectedOuput = tryTo<typename Tgt::rep>(outputValue);
-    if (expectedOuput.hasError()) {
-      return makeUnexpected(expectedOuput.error());
+    auto expectedOutput = tryTo<typename Tgt::rep>(outputValue);
+    if (expectedOutput.hasError()) {
+      return makeUnexpected(expectedOutput.error());
     }
 
-    return Tgt{expectedOuput.value()};
+    return Tgt{expectedOutput.value()};
   }
 }
 
diff --git a/folly/folly/cli/NestedCommandLineApp.cpp b/folly/folly/cli/NestedCommandLineApp.cpp
--- a/folly/folly/cli/NestedCommandLineApp.cpp
+++ b/folly/folly/cli/NestedCommandLineApp.cpp
@@ -331,25 +331,39 @@
   if (info.positionalOptions) {
     parser = parser.positional(*info.positionalOptions);
   }
+  try {
+    auto cmdOptions = parser.run();
 
-  auto cmdOptions = parser.run();
+    po::store(cmdOptions, vm);
+    po::notify(vm);
 
-  po::store(cmdOptions, vm);
-  po::notify(vm);
+    // If positional arguments are specified they should get mapped to a named
+    // arg and don't need to be double collected
+    auto cmdArgs = po::collect_unrecognized(
+        cmdOptions.options,
+        info.positionalOptions
+            ? po::exclude_positional
+            : po::include_positional);
 
-  // If positional arguments are specified they should get mapped to a named arg
-  // and don't need to be double collected
-  auto cmdArgs = po::collect_unrecognized(
-      cmdOptions.options,
-      info.positionalOptions ? po::exclude_positional : po::include_positional);
+    cmdArgs.insert(cmdArgs.end(), endArgs.begin(), endArgs.end());
 
-  cmdArgs.insert(cmdArgs.end(), endArgs.begin(), endArgs.end());
+    for (const auto& callback : callbackFunctions_) {
+      callback(cmd, vm, cmdArgs);
+    }
 
-  for (const auto& callback : callbackFunctions_) {
-    callback(cmd, vm, cmdArgs);
+    info.command(vm, cmdArgs);
+  } catch (const po::required_option& ex) {
+    // The top run() function won't be able to capture the command name.
+    // We include both program name and command name in the error message for
+    // better UX.
+    throw ProgramExit(
+        1,
+        fmt::format(
+            "Missing required option: '{}'. Run '{} {} --help' for help.",
+            ex.get_option_name(),
+            programName_,
+            cmd));
   }
-
-  info.command(vm, cmdArgs);
 }
 
 bool NestedCommandLineApp::isBuiltinCommand(const std::string& name) const {
diff --git a/folly/folly/cli/NestedCommandLineApp.h b/folly/folly/cli/NestedCommandLineApp.h
--- a/folly/folly/cli/NestedCommandLineApp.h
+++ b/folly/folly/cli/NestedCommandLineApp.h
@@ -117,11 +117,18 @@
     return globalOptions_;
   }
 
-  /*
+  /**
    * Return the commands map, so you can see the registered commands and get
    * access to their respective options descriptions.
    */
-  const std::map<std::string, CommandInfo>& commands() { return commands_; }
+  const std::map<std::string, CommandInfo>& commands() const {
+    return commands_;
+  }
+
+  /**
+   * Return the aliases map, so you can see the registered aliases.
+   */
+  const std::map<std::string, std::string>& aliases() const { return aliases_; }
 
   /**
    * Add a command.
diff --git a/folly/folly/cli/ProgramOptions.cpp b/folly/folly/cli/ProgramOptions.cpp
--- a/folly/folly/cli/ProgramOptions.cpp
+++ b/folly/folly/cli/ProgramOptions.cpp
@@ -232,14 +232,13 @@
   // clang-format on
 }
 
-typedef void (*FlagAdder)(
+using FlagAdder = void (*)(
     folly::gflags::CommandLineFlagInfo&&,
     po::options_description&,
     ProgramOptionsStyle);
 
 const std::unordered_map<std::string, FlagAdder> gFlagAdders = {
-#define X(NAME, TYPE) \
-  { NAME, addGFlag<TYPE> }
+#define X(NAME, TYPE) {NAME, addGFlag<TYPE>}
     X("bool", bool),
     X("int32", int32_t),
     X("int64", int64_t),
diff --git a/folly/folly/compression/Compression.cpp b/folly/folly/compression/Compression.cpp
--- a/folly/folly/compression/Compression.cpp
+++ b/folly/folly/compression/Compression.cpp
@@ -62,7 +62,6 @@
 #include <folly/compression/Utils.h>
 #include <folly/io/Cursor.h>
 #include <folly/lang/Bits.h>
-#include <folly/stop_watch.h>
 
 using folly::compression::detail::dataStartsWithLE;
 using folly::compression::detail::prefixToStringLE;
@@ -221,8 +220,9 @@
 
 void StreamCodec::assertStateIs(State expected) const {
   if (state_ != expected) {
-    throw std::logic_error(folly::to<std::string>(
-        "Codec: state is ", state_, "; expected state ", expected));
+    throw std::logic_error(
+        folly::to<std::string>(
+            "Codec: state is ", state_, "; expected state ", expected));
   }
 }
 
@@ -1188,8 +1188,9 @@
   lzma_ret const rc =
       lzma_easy_encoder(cstream_.get_pointer(), level_, LZMA_CHECK_NONE);
   if (rc != LZMA_OK) {
-    throw std::runtime_error(folly::to<std::string>(
-        "LZMA2StreamCodec: lzma_easy_encoder error: ", rc));
+    throw std::runtime_error(
+        folly::to<std::string>(
+            "LZMA2StreamCodec: lzma_easy_encoder error: ", rc));
   }
 }
 
@@ -1200,8 +1201,9 @@
   lzma_ret const rc = lzma_auto_decoder(
       dstream_.get_pointer(), std::numeric_limits<uint64_t>::max(), 0);
   if (rc != LZMA_OK) {
-    throw std::runtime_error(folly::to<std::string>(
-        "LZMA2StreamCodec: lzma_auto_decoder error: ", rc));
+    throw std::runtime_error(
+        folly::to<std::string>(
+            "LZMA2StreamCodec: lzma_auto_decoder error: ", rc));
   }
 }
 
@@ -1814,21 +1816,25 @@
   }
 
   // Check that none of the codecs are null
-  DCHECK(std::none_of(
-      codecs_.begin(), codecs_.end(), [](std::unique_ptr<Codec> const& codec) {
-        return codec == nullptr;
-      }));
+  DCHECK(
+      std::none_of(
+          codecs_.begin(),
+          codecs_.end(),
+          [](std::unique_ptr<Codec> const& codec) {
+            return codec == nullptr;
+          }));
 
   // Check that the terminal codec's type is not duplicated (with the exception
   // of USER_DEFINED).
   if (terminalCodec_) {
-    DCHECK(std::none_of(
-        codecs_.begin(),
-        codecs_.end(),
-        [&](std::unique_ptr<Codec> const& codec) {
-          return codec->type() != CodecType::USER_DEFINED &&
-              codec->type() == terminalCodec_->type();
-        }));
+    DCHECK(
+        std::none_of(
+            codecs_.begin(),
+            codecs_.end(),
+            [&](std::unique_ptr<Codec> const& codec) {
+              return codec->type() != CodecType::USER_DEFINED &&
+                  codec->type() == terminalCodec_->type();
+            }));
   }
 
   bool const terminalNeedsUncompressedLength =
diff --git a/folly/folly/compression/CompressionContextPool.h b/folly/folly/compression/CompressionContextPool.h
--- a/folly/folly/compression/CompressionContextPool.h
+++ b/folly/folly/compression/CompressionContextPool.h
@@ -17,21 +17,69 @@
 #pragma once
 
 #include <memory>
+#include <optional>
 
 #include <folly/Memory.h>
 #include <folly/Synchronized.h>
 
+/**
+ * Temporary implementation detail:
+ *
+ * The compression context pool singletons are actually used during static
+ * initialization time by other modules, so those singletons need to avoid
+ * SIOF issues. They do that by being marked `constinit`, so that they have
+ * already been constructed before static initialization / program execution
+ * begins. That means that the context pools need `constexpr` constructors.
+ * `std::vector` is supposed to have a `constexpr` constructor in C++20, but
+ * we don't seem to have it (everywhere).
+ *
+ * So while we can't rely on `constexpr std::vector::vector()`, we wrap the
+ * vector with a `std::optional`. When we can `constexpr` construct the vector,
+ * we do so. Otherwise, we construct it as empty and have to add checks to
+ * populate it later.
+ *
+ * When folly is only being built on platforms that have this, we can remove
+ * the `std::optional` shim etc.
+ */
+#ifndef FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR
+#if defined(__cpp_lib_constexpr_vector) && \
+    __cpp_lib_constexpr_vector >= 201907L && !defined(_MSC_VER)
+#define FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR 1
+#else
+#define FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR 0
+#endif
+#endif
+
 namespace folly {
 namespace compression {
 
-template <typename T, typename Creator, typename Deleter, typename Resetter>
+struct CompressionContextPoolDefaultCallback {
+  void operator()() const {}
+};
+
+constexpr size_t COMPRESSION_CONTEXT_POOL_CALLBACK_INTERVAL = 1024;
+
+/**
+ * This implementation is slow under contention. Except under uncontended
+ * scenarios, you shouldn't use it directly. You likely want to use the
+ * CompressionCoreLocalContextPool instead, which, behind the fast cache slots,
+ * is backed by this implementation.
+ */
+template <
+    typename T,
+    typename Creator,
+    typename Deleter,
+    typename Resetter,
+    typename Sizeof,
+    typename Callback = CompressionContextPoolDefaultCallback>
 class CompressionContextPool {
  private:
   using InternalRef = std::unique_ptr<T, Deleter>;
 
   class ReturnToPoolDeleter {
    public:
-    using Pool = CompressionContextPool<T, Creator, Deleter, Resetter>;
+    using Pool =
+        CompressionContextPool<T, Creator, Deleter, Resetter, Sizeof, Callback>;
 
     explicit ReturnToPoolDeleter(Pool* pool) : pool_(pool) { DCHECK(pool); }
 
@@ -48,65 +96,145 @@
   using Object = T;
   using Ref = std::unique_ptr<T, ReturnToPoolDeleter>;
 
-  explicit CompressionContextPool(
+  constexpr explicit CompressionContextPool(
       Creator creator = Creator(),
       Deleter deleter = Deleter(),
-      Resetter resetter = Resetter())
+      Resetter resetter = Resetter(),
+      Sizeof size_of = Sizeof(),
+      Callback callback = Callback())
       : creator_(std::move(creator)),
         deleter_(std::move(deleter)),
         resetter_(std::move(resetter)),
-        stack_(),
+        size_of_(std::move(size_of)),
+        callback_(std::move(callback)),
+        state_(),
         created_(0) {}
 
   Ref get() {
-    auto stack = stack_.wlock();
-    if (stack->empty()) {
-      T* t = creator_();
-      if (t == nullptr) {
-        throw_exception<std::bad_alloc>();
+    bool do_cb = false;
+    Ref ref{nullptr, get_deleter()};
+    {
+      auto lock = state_.wlock();
+#if !FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR
+      if (!lock->stack_) {
+        lock->stack_.emplace();
       }
-      created_++;
-      return Ref(t, get_deleter());
+#endif
+      auto& stack = *lock->stack_;
+      if (!stack.empty()) {
+        auto ptr = std::move(stack.back());
+        stack.pop_back();
+        do_cb = (lock->callback_counter_++ %
+                 COMPRESSION_CONTEXT_POOL_CALLBACK_INTERVAL) == 0;
+        if (!ptr) {
+          throw_exception<std::logic_error>(
+              "A nullptr snuck into our context pool!?!?");
+        }
+        ref = Ref(ptr.release(), get_deleter());
+      }
     }
-    auto ptr = std::move(stack->back());
-    stack->pop_back();
-    if (!ptr) {
-      throw_exception<std::logic_error>(
-          "A nullptr snuck into our context pool!?!?");
+    if (do_cb) {
+      callback_();
     }
-    return Ref(ptr.release(), get_deleter());
+    if (!ref) {
+      ref = create();
+    }
+    return ref;
   }
 
   size_t created_count() const { return created_.load(); }
 
-  size_t size() { return stack_.rlock()->size(); }
+  size_t size() {
+    auto lock = state_.rlock();
+#if !FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR
+    if (!lock->stack_) {
+      return 0;
+    }
+#endif
+    auto& stack = *lock->stack_;
+    return stack.size();
+  }
 
+  /// @returns the total bytes of memory used by the pool.
+  size_t bytes_used() const {
+    auto lock = state_.rlock();
+#if !FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR
+    if (!lock->stack_) {
+      return 0;
+    }
+#endif
+    auto& stack = *lock->stack_;
+    size_t bytes = 0;
+    for (auto& ptr : stack) {
+      bytes += size_of_(ptr.get());
+    }
+    return bytes;
+  }
+
   ReturnToPoolDeleter get_deleter() { return ReturnToPoolDeleter(this); }
 
-  Resetter& get_resetter() { return resetter_; }
+  const Resetter& get_resetter() { return resetter_; }
 
+  const Sizeof& get_sizeof() const { return size_of_; }
+
   void flush_deep() {
     flush_shallow();
     // no backing stack, so deep == shallow
   }
 
   void flush_shallow() {
-    auto stack = stack_.wlock();
-    stack->resize(0);
+    auto lock = state_.wlock();
+#if !FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR
+    if (!lock->stack_) {
+      return;
+    }
+#endif
+    auto& stack = *lock->stack_;
+    stack.resize(0);
   }
 
  private:
   void add(InternalRef ptr) {
     DCHECK(ptr);
     resetter_(ptr.get());
-    stack_.wlock()->push_back(std::move(ptr));
+    auto lock = state_.wlock();
+    // add() can only be called when we get a ref we created back, so the
+    // stack must already have been initialized. So we don't need to check.
+    auto& stack = *lock->stack_;
+    stack.push_back(std::move(ptr));
   }
 
-  Creator creator_;
-  Deleter deleter_;
-  Resetter resetter_;
+  Ref create() {
+    T* t = creator_();
+    if (t == nullptr) {
+      throw_exception<std::bad_alloc>();
+    }
+    created_++;
+    return Ref(t, get_deleter());
+  }
 
-  folly::Synchronized<std::vector<InternalRef>> stack_;
+  const Creator creator_;
+  const Deleter deleter_;
+  const Resetter resetter_;
+  const Sizeof size_of_;
+  const Callback callback_;
+
+  struct SyncState {
+    explicit constexpr SyncState()
+        :
+#if FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR
+          stack_(std::in_place, std::vector<InternalRef>{}),
+#else
+          stack_(),
+#endif
+          callback_counter_(0) {
+    }
+
+    std::optional<std::vector<InternalRef>> stack_;
+    size_t callback_counter_;
+  };
+
+  folly::Synchronized<SyncState> state_;
 
   std::atomic<size_t> created_;
 };
diff --git a/folly/folly/compression/CompressionContextPoolSingletons.cpp b/folly/folly/compression/CompressionContextPoolSingletons.cpp
--- a/folly/folly/compression/CompressionContextPoolSingletons.cpp
+++ b/folly/folly/compression/CompressionContextPoolSingletons.cpp
@@ -20,6 +20,7 @@
 
 #include <folly/Portability.h>
 #include <folly/memory/Malloc.h>
+#include <folly/system/HardwareConcurrency.h>
 
 #ifndef FOLLY_COMPRESSION_USE_HUGEPAGES
 #if defined(__linux__) && !defined(__ANDROID__)
@@ -37,19 +38,55 @@
 #ifndef ZSTD_STATIC_LINKING_ONLY
 #define ZSTD_STATIC_LINKING_ONLY
 #endif
-#include <zstd.h>
+#include <zstd.h> // @donotremove
 #endif
 
+#if FOLLY_HAVE_WEAK_SYMBOLS
+FOLLY_ATTR_WEAK double
+folly_zstd_cctx_pool_stripes_cpu_multiplier_default() noexcept;
+FOLLY_ATTR_WEAK double
+folly_zstd_dctx_pool_stripes_cpu_multiplier_default() noexcept;
+#else
+static double (
+    *folly_zstd_cctx_pool_stripes_cpu_multiplier_default)() noexcept = nullptr;
+static double (
+    *folly_zstd_dctx_pool_stripes_cpu_multiplier_default)() noexcept = nullptr;
+#endif
+
+FOLLY_GFLAGS_DEFINE_double(
+    folly_zstd_cctx_pool_stripes_cpu_multiplier,
+    folly_zstd_cctx_pool_stripes_cpu_multiplier_default
+        ? folly_zstd_cctx_pool_stripes_cpu_multiplier_default()
+        : 0.0,
+    "Number of stripes for compression context pool specified as a multiplier of hardware concurrency.");
+FOLLY_GFLAGS_DEFINE_double(
+    folly_zstd_dctx_pool_stripes_cpu_multiplier,
+    folly_zstd_dctx_pool_stripes_cpu_multiplier_default
+        ? folly_zstd_dctx_pool_stripes_cpu_multiplier_default()
+        : 0.0,
+    "Number of stripes for decompression context pool specified as a multiplier of hardware concurrency.");
+
 namespace folly {
 namespace compression {
 namespace contexts {
 
 #if FOLLY_HAVE_LIBZSTD
 namespace {
-// These objects have no static dependencies and therefore no SIOF issues.
-ZSTD_CCtx_Pool zstd_cctx_pool_singleton;
-ZSTD_DCtx_Pool zstd_dctx_pool_singleton;
 
+// These objects must be constinit in order to be SIOF-safe, since they are
+// accessed during the static initialization of other translation units.
+struct ZSTD_cctx_pool_singleton : ZSTD_CCtx_Pool {
+  constexpr ZSTD_cctx_pool_singleton()
+      : ZSTD_CCtx_Pool{8, {}, {}, {}, {}, ZSTD_CCtx_Pool_Callback{this}} {}
+};
+struct ZSTD_dctx_pool_singleton : ZSTD_DCtx_Pool {
+  constexpr ZSTD_dctx_pool_singleton()
+      : ZSTD_DCtx_Pool{8, {}, {}, {}, {}, ZSTD_DCtx_Pool_Callback{this}} {}
+};
+
+constinit ZSTD_cctx_pool_singleton zstd_cctx_pool_singleton;
+constinit ZSTD_dctx_pool_singleton zstd_dctx_pool_singleton;
+
 #if FOLLY_COMPRESSION_USE_HUGEPAGES
 constexpr bool use_huge_pages = kIsArchAmd64;
 
@@ -74,11 +111,11 @@
   }
 }
 
-ZSTD_customMem huge_page_custom_mem = (use_huge_pages && usingJEMalloc())
+const ZSTD_customMem huge_page_custom_mem = (use_huge_pages && usingJEMalloc())
     ? (ZSTD_customMem){huge_page_alloc, huge_page_free, nullptr}
     : ZSTD_defaultCMem;
 #else
-ZSTD_customMem huge_page_custom_mem = ZSTD_defaultCMem;
+const ZSTD_customMem huge_page_custom_mem = ZSTD_defaultCMem;
 #endif
 
 } // anonymous namespace
@@ -109,6 +146,40 @@
   size_t const err = ZSTD_DCtx_reset(ctx, ZSTD_reset_session_and_parameters);
   assert(!ZSTD_isError(err)); // This function doesn't actually fail
   (void)err;
+}
+
+size_t ZSTD_CCtx_Sizeof::operator()(const ZSTD_CCtx* ctx) const noexcept {
+  return ZSTD_sizeof_CCtx(ctx);
+}
+
+size_t ZSTD_DCtx_Sizeof::operator()(const ZSTD_DCtx* ctx) const noexcept {
+  return ZSTD_sizeof_DCtx(ctx);
+}
+
+void ZSTD_CCtx_Pool_Callback::operator()() const {
+  if (pool_) {
+    if (auto multiplier = FLAGS_folly_zstd_cctx_pool_stripes_cpu_multiplier;
+        multiplier > 0.0) {
+      static const size_t num_cores = folly::hardware_concurrency();
+      const size_t num_stripes = std::min(
+          static_cast<size_t>(std::ceil(multiplier * num_cores)),
+          ZSTD_CCtx_Pool::kMaxNumStripes);
+      pool_->setSize(num_stripes);
+    }
+  }
+}
+
+void ZSTD_DCtx_Pool_Callback::operator()() const {
+  if (pool_) {
+    if (auto multiplier = FLAGS_folly_zstd_dctx_pool_stripes_cpu_multiplier;
+        multiplier > 0.0) {
+      static const size_t num_cores = folly::hardware_concurrency();
+      const size_t num_stripes = std::min(
+          static_cast<size_t>(std::ceil(multiplier * num_cores)),
+          ZSTD_DCtx_Pool::kMaxNumStripes);
+      pool_->setSize(num_stripes);
+    }
+  }
 }
 
 ZSTD_CCtx_Pool::Ref getZSTD_CCtx() {
diff --git a/folly/folly/compression/CompressionContextPoolSingletons.h b/folly/folly/compression/CompressionContextPoolSingletons.h
--- a/folly/folly/compression/CompressionContextPoolSingletons.h
+++ b/folly/folly/compression/CompressionContextPoolSingletons.h
@@ -23,10 +23,18 @@
 #endif
 
 #include <folly/compression/CompressionCoreLocalContextPool.h>
+#include <folly/portability/GFlags.h>
 
 // When this header is present, folly/compression/Compression.h defines
 // FOLLY_COMPRESSION_HAS_CONTEXT_POOL_SINGLETONS.
 
+// These flags allow for tuning the number of stripes in the singleton
+// context pool. This is expressed as a multiplier of CPU hardware
+// concurrency, e.g. a setting of 0.1 on a machine with 50 cores results
+// in 5 stripes in the context pool singleton.
+FOLLY_GFLAGS_DECLARE_double(folly_zstd_cctx_pool_stripes_cpu_multiplier);
+FOLLY_GFLAGS_DECLARE_double(folly_zstd_dctx_pool_stripes_cpu_multiplier);
+
 namespace folly {
 namespace compression {
 namespace contexts {
@@ -60,18 +68,52 @@
   void operator()(ZSTD_DCtx* ctx) const noexcept;
 };
 
+struct ZSTD_CCtx_Sizeof {
+  size_t operator()(const ZSTD_CCtx* ctx) const noexcept;
+};
+
+struct ZSTD_DCtx_Sizeof {
+  size_t operator()(const ZSTD_DCtx* ctx) const noexcept;
+};
+
+class ZSTD_CCtx_Pool_Callback {
+ public:
+  explicit constexpr ZSTD_CCtx_Pool_Callback(
+      CompressionCoreLocalContextPoolBase* pool)
+      : pool_(pool) {}
+
+  void operator()() const;
+
+ private:
+  CompressionCoreLocalContextPoolBase* pool_;
+};
+
+struct ZSTD_DCtx_Pool_Callback {
+ public:
+  explicit constexpr ZSTD_DCtx_Pool_Callback(
+      CompressionCoreLocalContextPoolBase* pool)
+      : pool_(pool) {}
+
+  void operator()() const;
+
+ private:
+  CompressionCoreLocalContextPoolBase* pool_;
+};
+
 using ZSTD_CCtx_Pool = CompressionCoreLocalContextPool<
     ZSTD_CCtx,
     ZSTD_CCtx_Creator,
     ZSTD_CCtx_Deleter,
     ZSTD_CCtx_Resetter,
-    4>;
+    ZSTD_CCtx_Sizeof,
+    ZSTD_CCtx_Pool_Callback>;
 using ZSTD_DCtx_Pool = CompressionCoreLocalContextPool<
     ZSTD_DCtx,
     ZSTD_DCtx_Creator,
     ZSTD_DCtx_Deleter,
     ZSTD_DCtx_Resetter,
-    16>;
+    ZSTD_DCtx_Sizeof,
+    ZSTD_DCtx_Pool_Callback>;
 
 /**
  * Returns a clean ZSTD_CCtx.
diff --git a/folly/folly/compression/CompressionCoreLocalContextPool.h b/folly/folly/compression/CompressionCoreLocalContextPool.h
--- a/folly/folly/compression/CompressionCoreLocalContextPool.h
+++ b/folly/folly/compression/CompressionCoreLocalContextPool.h
@@ -23,6 +23,17 @@
 namespace compression {
 
 /**
+ * Non-templated base class which allows for generic interaction with context
+ * pool instances.
+ */
+class CompressionCoreLocalContextPoolBase {
+ public:
+  virtual ~CompressionCoreLocalContextPoolBase() = default;
+
+  virtual void setSize(size_t size) = 0;
+};
+
+/**
  * This class is intended to reduce contention on reserving a compression
  * context and improve cache locality (but maybe not hotness) of the contexts
  * it manages.
@@ -41,17 +52,17 @@
     typename Creator,
     typename Deleter,
     typename Resetter,
-    size_t NumStripes = 8>
-class CompressionCoreLocalContextPool {
+    typename Sizeof,
+    typename Callback = CompressionContextPoolDefaultCallback>
+class CompressionCoreLocalContextPool
+    : public CompressionCoreLocalContextPoolBase {
  private:
   /**
    * Force each pointer to be on a different cache line.
    */
   class alignas(folly::hardware_destructive_interference_size) Storage {
    public:
-    Storage() : ptr(nullptr) {}
-
-    std::atomic<T*> ptr;
+    std::atomic<T*> ptr{nullptr};
   };
 
   class ReturnToPoolDeleter {
@@ -61,7 +72,8 @@
         Creator,
         Deleter,
         Resetter,
-        NumStripes>;
+        Sizeof,
+        Callback>;
 
     explicit ReturnToPoolDeleter(Pool* pool) : pool_(pool) { DCHECK(pool_); }
 
@@ -71,21 +83,37 @@
     Pool* pool_;
   };
 
-  using BackingPool = CompressionContextPool<T, Creator, Deleter, Resetter>;
+  using BackingPool =
+      CompressionContextPool<T, Creator, Deleter, Resetter, Sizeof, Callback>;
   using BackingPoolRef = typename BackingPool::Ref;
 
  public:
+  /**
+   * The max size is derived from maximum stripes for folly::AccessSpreader.
+   */
+  static constexpr size_t kMaxNumStripes =
+      folly::detail::AccessSpreaderBase::kMaxCpus;
+
   using Object = T;
   using Ref = std::unique_ptr<T, ReturnToPoolDeleter>;
 
-  explicit CompressionCoreLocalContextPool(
+  constexpr explicit CompressionCoreLocalContextPool(
+      size_t numStripes = 8,
       Creator creator = Creator(),
       Deleter deleter = Deleter(),
-      Resetter resetter = Resetter())
-      : pool_(std::move(creator), std::move(deleter), std::move(resetter)),
+      Resetter resetter = Resetter(),
+      Sizeof size_of = Sizeof(),
+      Callback callback = Callback())
+      : numStripes_(numStripes),
+        pool_(
+            std::move(creator),
+            std::move(deleter),
+            std::move(resetter),
+            std::move(size_of),
+            std::move(callback)),
         caches_() {}
 
-  ~CompressionCoreLocalContextPool() { flush_shallow(); }
+  ~CompressionCoreLocalContextPool() override { flush_shallow(); }
 
   Ref get() {
     auto ptr = local().ptr.exchange(nullptr);
@@ -99,6 +127,28 @@
 
   Ref getNull() { return Ref(nullptr, get_deleter()); }
 
+  /**
+   * Update the number of stripes. This will flush the pool if the stripe count
+   * changes.
+   */
+  void setSize(size_t numStripes) override {
+    if (numStripes == 0) {
+      throw_exception<std::invalid_argument>(
+          "CompressionCoreLocalContextPool must have at least 1 stripe");
+    }
+    if (numStripes > kMaxNumStripes) {
+      DCHECK(false);
+      numStripes = kMaxNumStripes;
+    }
+
+    auto before = numStripes_.exchange(numStripes);
+    if (before != numStripes) {
+      flush_shallow();
+    }
+  }
+
+  size_t cacheSize() const { return numStripes_.load(); }
+
   size_t created_count() const { return pool_.created_count(); }
 
   void flush_deep() {
@@ -114,6 +164,16 @@
     }
   }
 
+  size_t bytes_used() const {
+    size_t bytes = pool_.bytes_used();
+    for (const auto& cache : caches_) {
+      if (auto ptr = cache.ptr.load()) {
+        bytes += pool_.get_sizeof()(ptr);
+      }
+    }
+    return bytes;
+  }
+
  private:
   ReturnToPoolDeleter get_deleter() { return ReturnToPoolDeleter(this); }
 
@@ -131,12 +191,21 @@
   }
 
   Storage& local() {
-    const auto idx = folly::AccessSpreader<>::cachedCurrent(NumStripes);
+    // Note that cachedCurrent(0) is valid, so this should be SIOF safe.
+    const auto idx = folly::AccessSpreader<>::cachedCurrent(numStripes_);
     return caches_[idx];
   }
 
+  relaxed_atomic<size_t> numStripes_;
   BackingPool pool_;
-  std::array<Storage, NumStripes> caches_{};
+
+  /**
+   * context_pool_max_num_stripes number of stripes are allocated to the
+   * underlying stripes array. However, only the lower numStripes_ indices are
+   * used to actually stripe the pool. This allows us to statically create
+   * singletons while providing flexibility around how many stripes there are.
+   */
+  std::array<Storage, kMaxNumStripes> caches_;
 };
 } // namespace compression
 } // namespace folly
diff --git a/folly/folly/compression/QuotientMultiSet-inl.h b/folly/folly/compression/QuotientMultiSet-inl.h
--- a/folly/folly/compression/QuotientMultiSet-inl.h
+++ b/folly/folly/compression/QuotientMultiSet-inl.h
@@ -251,8 +251,8 @@
 
 template <class Instructions>
 auto QuotientMultiSet<Instructions>::findRunend(
-    uint64_t occupiedRank,
-    uint64_t firstRunend) const -> std::pair<uint64_t, const Block*> {
+    uint64_t occupiedRank, uint64_t firstRunend) const
+    -> std::pair<uint64_t, const Block*> {
   // Look for the right runend.
   size_t slotBlockIndex = firstRunend / kBlockSize;
   auto block = getBlock(slotBlockIndex);
diff --git a/folly/folly/compression/Zlib.cpp b/folly/folly/compression/Zlib.cpp
--- a/folly/folly/compression/Zlib.cpp
+++ b/folly/folly/compression/Zlib.cpp
@@ -226,16 +226,20 @@
   // is also not supported for gzip and raw deflation.
   // Hence, the codec supports only 9..15.
   if (!inBounds(options_.windowSize, 9, 15)) {
-    throw std::invalid_argument(to<std::string>(
-        "ZlibStreamCodec: invalid windowSize option: ", options.windowSize));
+    throw std::invalid_argument(
+        to<std::string>(
+            "ZlibStreamCodec: invalid windowSize option: ",
+            options.windowSize));
   }
   if (!inBounds(options_.memLevel, 1, 9)) {
-    throw std::invalid_argument(to<std::string>(
-        "ZlibStreamCodec: invalid memLevel option: ", options.memLevel));
+    throw std::invalid_argument(
+        to<std::string>(
+            "ZlibStreamCodec: invalid memLevel option: ", options.memLevel));
   }
   if (!isValidStrategy(options_.strategy)) {
-    throw std::invalid_argument(to<std::string>(
-        "ZlibStreamCodec: invalid strategy: ", options.strategy));
+    throw std::invalid_argument(
+        to<std::string>(
+            "ZlibStreamCodec: invalid strategy: ", options.strategy));
   }
 }
 
diff --git a/folly/folly/compression/Zstd.cpp b/folly/folly/compression/Zstd.cpp
--- a/folly/folly/compression/Zstd.cpp
+++ b/folly/folly/compression/Zstd.cpp
@@ -21,8 +21,6 @@
 #include <stdexcept>
 #include <string>
 
-#include <zstd.h>
-
 #include <folly/Conv.h>
 #include <folly/Range.h>
 #include <folly/ScopeGuard.h>
diff --git a/folly/folly/compression/elias_fano/BitVectorCoding.h b/folly/folly/compression/elias_fano/BitVectorCoding.h
--- a/folly/folly/compression/elias_fano/BitVectorCoding.h
+++ b/folly/folly/compression/elias_fano/BitVectorCoding.h
@@ -65,8 +65,8 @@
   Pointer forwardPointers = nullptr;
 };
 
-typedef BitVectorCompressedListBase<const uint8_t*> BitVectorCompressedList;
-typedef BitVectorCompressedListBase<uint8_t*> MutableBitVectorCompressedList;
+using BitVectorCompressedList = BitVectorCompressedListBase<const uint8_t*>;
+using MutableBitVectorCompressedList = BitVectorCompressedListBase<uint8_t*>;
 
 template <
     class Value,
@@ -78,11 +78,11 @@
       std::is_integral<Value>::value && std::is_unsigned<Value>::value,
       "Value should be unsigned integral");
 
-  typedef BitVectorCompressedList CompressedList;
-  typedef MutableBitVectorCompressedList MutableCompressedList;
+  using CompressedList = BitVectorCompressedList;
+  using MutableCompressedList = MutableBitVectorCompressedList;
 
-  typedef Value ValueType;
-  typedef SkipValue SkipValueType;
+  using ValueType = Value;
+  using SkipValueType = SkipValue;
   struct Layout;
 
   static constexpr size_t skipQuantum = kSkipQuantum;
@@ -242,11 +242,11 @@
     : detail::ForwardPointers<Encoder::forwardQuantum>,
       detail::SkipPointers<Encoder::skipQuantum> {
  public:
-  typedef Encoder EncoderType;
-  typedef typename Encoder::ValueType ValueType;
+  using EncoderType = Encoder;
+  using ValueType = typename Encoder::ValueType;
   // A bitvector can only be as large as its largest value.
-  typedef typename Encoder::ValueType SizeType;
-  typedef typename Encoder::SkipValueType SkipValueType;
+  using SizeType = typename Encoder::ValueType;
+  using SkipValueType = typename Encoder::SkipValueType;
 
   explicit BitVectorReader(const typename Encoder::CompressedList& list)
       : detail::ForwardPointers<Encoder::forwardQuantum>(list.forwardPointers),
diff --git a/folly/folly/compression/elias_fano/EliasFanoCoding.h b/folly/folly/compression/elias_fano/EliasFanoCoding.h
--- a/folly/folly/compression/elias_fano/EliasFanoCoding.h
+++ b/folly/folly/compression/elias_fano/EliasFanoCoding.h
@@ -879,7 +879,7 @@
     return readLowerPart(position()) | (upper_.value() << numLowerBits_);
   }
 
-  // Ordering of fields is counter-intutive but it optimizes the layout.
+  // Ordering of fields is counter-intuitive but it optimizes the layout.
   UpperBitsReader upper_;
   const uint8_t* const lower_;
   ValueType value_;
diff --git a/folly/folly/concurrency/AtomicSharedPtr.h b/folly/folly/concurrency/AtomicSharedPtr.h
--- a/folly/folly/concurrency/AtomicSharedPtr.h
+++ b/folly/folly/concurrency/AtomicSharedPtr.h
@@ -33,6 +33,8 @@
 #define FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED 0
 #endif
 
+namespace folly {
+
 #if FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED
 
 /*
@@ -77,7 +79,6 @@
 
 // A note on noexcept: If the pointer is an aliased pointer,
 // store() will allocate.  Otherwise is noexcept.
-namespace folly {
 
 template <
     typename T,
@@ -393,12 +394,8 @@
   mutable AtomicStruct<PackedPtr, Atom> ptr_;
 };
 
-} // namespace folly
-
 #else
 
-namespace folly {
-
 template <typename T>
 class atomic_shared_ptr {
  private:
@@ -479,6 +476,9 @@
   }
 };
 
-} // namespace folly
-
 #endif // FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED
+
+template <typename T>
+atomic_shared_ptr(std::shared_ptr<T>) -> atomic_shared_ptr<T>;
+
+} // namespace folly
diff --git a/folly/folly/concurrency/CacheLocality.cpp b/folly/folly/concurrency/CacheLocality.cpp
--- a/folly/folly/concurrency/CacheLocality.cpp
+++ b/folly/folly/concurrency/CacheLocality.cpp
@@ -20,6 +20,9 @@
 #define _GNU_SOURCE 1 // for RTLD_NOLOAD
 #include <dlfcn.h>
 #endif
+#include <string.h>
+
+#include <filesystem>
 #include <fstream>
 #include <mutex>
 #include <numeric>
@@ -31,9 +34,11 @@
 #include <folly/Indestructible.h>
 #include <folly/Memory.h>
 #include <folly/ScopeGuard.h>
-#include <folly/detail/StaticSingletonManager.h>
+#include <folly/container/Reserve.h>
 #include <folly/hash/Hash.h>
 #include <folly/lang/Exception.h>
+#include <folly/portability/Fcntl.h>
+#include <folly/portability/FmtCompile.h>
 #include <folly/portability/Unistd.h>
 #include <folly/system/ThreadId.h>
 
@@ -42,16 +47,19 @@
 ///////////// CacheLocality
 
 /// Returns the CacheLocality information best for this machine
-static CacheLocality getSystemLocalityInfo() {
+CacheLocality CacheLocality::readSystemLocalityInfo() {
   if (kIsLinux) {
-    // First try to parse /proc/cpuinfo.
-    // If that fails, then try to parse /sys/devices/.
-    // The latter is slower but more accurate.
-    try {
-      return CacheLocality::readFromProcCpuinfo();
-    } catch (...) {
-      // /proc/cpuinfo might be non-standard
-      // lets try with sysfs /sys/devices/cpu
+    if (kIsArchAmd64 || kIsArchX86) {
+      // First try to parse /proc/cpuinfo.
+      // But only on arch's where the file has cpu topology hints.
+      // If that fails, then try to parse /sys/devices/.
+      // The latter is slower but more accurate.
+      try {
+        return CacheLocality::readFromProcCpuinfo();
+      } catch (...) {
+        // /proc/cpuinfo might be non-standard
+        // lets try with sysfs /sys/devices/cpu
+      }
     }
 
     try {
@@ -83,7 +91,7 @@
   if (value != nullptr) {
     return *value;
   }
-  auto next = new CacheLocality(getSystemLocalityInfo());
+  auto next = new CacheLocality(readSystemLocalityInfo());
   if (cache.compare_exchange_strong(value, next, std::memory_order_acq_rel)) {
     return *next;
   }
@@ -172,20 +180,77 @@
   return val;
 }
 
-CacheLocality CacheLocality::readFromSysfsTree(
-    const std::function<std::string(std::string const&)>& mapping) {
+CacheLocality CacheLocality::readFromSysfsTree(std::string_view root) {
+#if defined(_WIN32)
+  // windows does not have openat and open flag constants
+  return CacheLocality::uniform(0);
+#else
+
   // the list of cache equivalence classes, where equivalence classes
   // are named by the smallest cpu in the class
   std::vector<std::vector<size_t>> equivClassesByCpu;
 
+  auto checkNoEnt = [](std::string_view name) {
+    auto err = errno;
+    if (err != ENOENT) {
+      throw std::runtime_error(
+          fmt::format(
+              "unexpected error while opening {}: {}", name, strerror(err)));
+    }
+  };
+
+  // Reads the first 64 bytes of the file.
+  auto rdfile64 = [&](int dirfd, const std::string& name) {
+    const auto fd = ::openat(dirfd, name.c_str(), O_CLOEXEC, O_RDONLY);
+    if (fd < 0) {
+      checkNoEnt(name);
+      return std::string(); // stop condition for the inner loop below
+    }
+    SCOPE_EXIT {
+      ::close(fd);
+    };
+
+    alignas(64) char buf[64];
+    int ret = 0;
+    do {
+      ret = ::pread(fd, buf, sizeof(buf), 0);
+    } while (ret < 0 && errno == EINTR);
+    if (ret < 0) {
+      return std::string();
+    }
+    return std::string(buf, to_unsigned(ret));
+  };
+
+  auto subroot = std::filesystem::path(root) / "sys/devices/system/cpu";
+  const int allfd = ::open(subroot.c_str(), O_DIRECTORY | O_CLOEXEC, O_RDONLY);
+  if (allfd < 0) {
+    auto err = errno;
+    throw std::runtime_error(
+        fmt::format("unable to open sysfs: {}", strerror(err)));
+  }
+  SCOPE_EXIT {
+    ::close(allfd);
+  };
+
+  size_t maxindex = 0;
   for (size_t cpu = 0;; ++cpu) {
+    auto cpuroot = fmt::format(FMT_COMPILE("cpu{}/cache"), cpu);
+    const int cpufd =
+        ::openat(allfd, cpuroot.c_str(), O_DIRECTORY | O_CLOEXEC, O_RDONLY);
+    if (cpufd < 0) {
+      checkNoEnt(cpuroot);
+      break;
+    }
+    SCOPE_EXIT {
+      ::close(cpufd);
+    };
+
     std::vector<size_t> levels;
+    grow_capacity_by(levels, maxindex);
     for (size_t index = 0;; ++index) {
-      auto dir = fmt::format(
-          "/sys/devices/system/cpu/cpu{}/cache/index{}/", cpu, index);
-      auto cacheType = mapping(dir + "type");
-      auto equivStr = mapping(dir + "shared_cpu_list");
-      if (cacheType.empty() || equivStr.empty()) {
+      auto dir = fmt::format(FMT_COMPILE("index{}/"), index);
+      auto cacheType = rdfile64(cpufd, dir + "type");
+      if (cacheType.empty()) {
         // no more caches
         break;
       }
@@ -193,9 +258,16 @@
         // cacheType in { "Data", "Instruction", "Unified" }. skip icache
         continue;
       }
+      // only try to read the second file once we know we will need it
+      auto equivStr = rdfile64(cpufd, dir + "shared_cpu_list");
+      if (equivStr.empty()) {
+        // no more caches
+        break;
+      }
       auto equiv = parseLeadingNumber(equivStr);
       levels.push_back(equiv);
     }
+    maxindex = std::max(maxindex, levels.size());
 
     if (levels.empty()) {
       // no levels at all for this cpu, we must be done
@@ -209,15 +281,12 @@
   }
 
   return CacheLocality{std::move(equivClassesByCpu)};
+
+#endif
 }
 
 CacheLocality CacheLocality::readFromSysfs() {
-  return readFromSysfsTree([](std::string const& name) {
-    std::ifstream xi(name.c_str());
-    std::string rv;
-    std::getline(xi, rv);
-    return rv;
-  });
+  return readFromSysfsTree();
 }
 
 namespace {
@@ -520,6 +589,11 @@
       folly::aligned_free(block);
     }
   }
+
+  SimpleAllocator(const SimpleAllocator&) = delete;
+  SimpleAllocator& operator=(const SimpleAllocator&) = delete;
+  SimpleAllocator(SimpleAllocator&&) = delete;
+  SimpleAllocator& operator=(SimpleAllocator&&) = delete;
 
   void* allocate() {
     std::lock_guard g(m_);
diff --git a/folly/folly/concurrency/CacheLocality.h b/folly/folly/concurrency/CacheLocality.h
--- a/folly/folly/concurrency/CacheLocality.h
+++ b/folly/folly/concurrency/CacheLocality.h
@@ -23,6 +23,7 @@
 #include <functional>
 #include <limits>
 #include <string>
+#include <string_view>
 #include <type_traits>
 #include <vector>
 
@@ -35,10 +36,10 @@
 
 // This file contains several classes that might be useful if you are
 // trying to dynamically optimize cache locality: CacheLocality reads
-// cache sharing information from sysfs to determine how CPUs should be
-// grouped to minimize contention, Getcpu provides fast access to the
-// current CPU via __vdso_getcpu, and AccessSpreader uses these two to
-// optimally spread accesses among a predetermined number of stripes.
+// cache sharing information from procfs or sysfs to determine how CPUs
+// should be grouped to minimize contention, Getcpu provides fast access
+// to the current CPU via __vdso_getcpu, and AccessSpreader uses these two
+// to optimally spread accesses among a predetermined number of stripes.
 //
 // AccessSpreader<>::current(n) microbenchmarks at 22 nanos, which is
 // substantially less than the cost of a cache miss.  This means that we
@@ -82,9 +83,9 @@
   std::vector<std::vector<size_t>> equivClassesByCpu;
 
   /// Returns the best CacheLocality information available for the current
-  /// system, cached for fast access.  This will be loaded from sysfs if
-  /// possible, otherwise it will be correct in the number of CPUs but
-  /// not in their sharing structure.
+  /// system, cached for fast access.  This will be loaded from procfs or
+  /// sysfs if possible, otherwise it will be correct in the number of CPUs
+  /// but not in their sharing structure.
   ///
   /// If you are into yo dawgs, this is a shared cache of the local
   /// locality of the shared caches.
@@ -93,11 +94,17 @@
   /// repeatable CacheLocality structure during testing.  Rather than
   /// inject the type of the CacheLocality provider into every data type
   /// that transitively uses it, all components select between the default
-  /// sysfs implementation and a deterministic implementation by keying
+  /// procfs/sysfs implementation and a deterministic implementation by keying
   /// off the type of the underlying atomic.  See DeterministicScheduler.
   template <template <typename> class Atom = std::atomic>
   static const CacheLocality& system();
 
+  /// Returns the best CacheLocality information available for the current
+  /// system.  This will be loaded from procfs or sysfs if possible, otherwise
+  /// it will be correct in the number of CPUs but not in their sharing
+  /// structure.
+  static CacheLocality readSystemLocalityInfo();
+
   /// Reads CacheLocality information from a tree structured like
   /// the sysfs filesystem.  The provided function will be evaluated
   /// for each sysfs file that needs to be queried.  The function
@@ -106,8 +113,7 @@
   /// not exist.  The function will be called with paths of the form
   /// /sys/devices/system/cpu/cpu*/cache/index*/{type,shared_cpu_list} .
   /// Throws an exception if no caches can be parsed at all.
-  static CacheLocality readFromSysfsTree(
-      const std::function<std::string(std::string const&)>& mapping);
+  static CacheLocality readFromSysfsTree(std::string_view root = "/");
 
   /// Reads CacheLocality information from the real sysfs filesystem.
   /// Throws an exception if no cache information can be loaded.
@@ -180,7 +186,7 @@
 namespace detail {
 
 class AccessSpreaderBase {
- protected:
+ public:
   /// If there are more cpus than this nothing will crash, but there
   /// might be unnecessary sharing
   enum {
@@ -188,6 +194,7 @@
     kMaxCpus = kIsMobile ? 16 : 256,
   };
 
+ protected:
   using CompactStripe = uint8_t;
 
   static_assert(
@@ -467,10 +474,15 @@
   };
 };
 
-class FOLLY_NODISCARD CoreAllocatorGuard {
+class [[nodiscard]] CoreAllocatorGuard {
  public:
   CoreAllocatorGuard(size_t numStripes, size_t stripe);
   ~CoreAllocatorGuard();
+
+  CoreAllocatorGuard(const CoreAllocatorGuard&) = delete;
+  CoreAllocatorGuard& operator=(const CoreAllocatorGuard&) = delete;
+  CoreAllocatorGuard(CoreAllocatorGuard&&) = delete;
+  CoreAllocatorGuard& operator=(CoreAllocatorGuard&&) = delete;
 
  private:
   friend void* detail::coreMallocFromGuard(size_t size);
diff --git a/folly/folly/concurrency/ConcurrentHashMap.h b/folly/folly/concurrency/ConcurrentHashMap.h
--- a/folly/folly/concurrency/ConcurrentHashMap.h
+++ b/folly/folly/concurrency/ConcurrentHashMap.h
@@ -137,10 +137,8 @@
         typename,
         typename,
         typename,
-        template <typename>
-        class,
-        class>
-    class Impl = detail::concurrenthashmap::bucket::BucketTable>
+        template <typename> class,
+        class> class Impl = detail::concurrenthashmap::bucket::BucketTable>
 class ConcurrentHashMap {
   using SegmentT = detail::ConcurrentHashMapSegment<
       KeyType,
@@ -166,13 +164,13 @@
  public:
   class ConstIterator;
 
-  typedef KeyType key_type;
-  typedef ValueType mapped_type;
-  typedef std::pair<const KeyType, ValueType> value_type;
-  typedef std::size_t size_type;
-  typedef HashFn hasher;
-  typedef KeyEqual key_equal;
-  typedef ConstIterator const_iterator;
+  using key_type = KeyType;
+  using mapped_type = ValueType;
+  using value_type = std::pair<const KeyType, ValueType>;
+  using size_type = std::size_t;
+  using hasher = HashFn;
+  using key_equal = KeyEqual;
+  using const_iterator = ConstIterator;
 
  private:
   template <typename K, typename T>
@@ -295,6 +293,16 @@
     return findImpl(k);
   }
 
+  /*
+   * Intentionally marked as deleted to guard against common misuse.
+   *
+   * Checking for key existence via contains() is unsafe in concurrent
+   * contexts. The key may be erased between contains() and subsequent
+   * operations. Use find() and its iterator to ensure safe access and
+   * prevent race conditions.
+   */
+  bool contains(const KeyType& k) const = delete;
+
   ConstIterator cend() const noexcept { return ConstIterator(NumShards); }
 
   ConstIterator cbegin() const noexcept { return ConstIterator(this); }
@@ -513,16 +521,12 @@
 
   // Erase if and only if key k is equal to expected
   size_type erase_if_equal(const key_type& k, const ValueType& expected) {
-    return erase_key_if(k, [&expected](const ValueType& v) {
-      return v == expected;
-    });
+    return erase_key_if(k, detail::concurrenthashmap::EqualTo1{expected});
   }
 
   template <typename K, EnableHeterogeneousErase<K, int> = 0>
   size_type erase_if_equal(const K& k, const ValueType& expected) {
-    return erase_key_if(k, [&expected](const ValueType& v) {
-      return v == expected;
-    });
+    return erase_key_if(k, detail::concurrenthashmap::EqualTo1{expected});
   }
 
   // Erase if predicate evaluates to true on the existing value
@@ -553,8 +557,9 @@
 
   void reserve(size_t count) {
     count = count >> ShardBits;
-    if (!count)
+    if (!count) {
       return;
+    }
     uint64_t begin = beginSeg_.load(std::memory_order_acquire);
     uint64_t end = endSeg_.load(std::memory_order_acquire);
     for (uint64_t i = begin; i < end; ++i) {
@@ -837,7 +842,9 @@
     ShardBits,
     Atom,
     Mutex,
-#if (FOLLY_SSE_PREREQ(4, 2) || FOLLY_AARCH64) && \
+#if (                                                        \
+    FOLLY_SSE_PREREQ(4, 2) ||                                \
+    (FOLLY_AARCH64 && FOLLY_F14_CRC_INTRINSIC_AVAILABLE)) && \
     FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
     detail::concurrenthashmap::simd::SIMDTable
 #else
diff --git a/folly/folly/concurrency/SingletonRelaxedCounter.h b/folly/folly/concurrency/SingletonRelaxedCounter.h
--- a/folly/folly/concurrency/SingletonRelaxedCounter.h
+++ b/folly/folly/concurrency/SingletonRelaxedCounter.h
@@ -80,9 +80,10 @@
   using GetLifetime = LocalLifetime&();
 
   struct Arg {
-    GetGlobal& global;
-    GetLocal& local;
-    GetLifetime& lifetime;
+    //  To be constexpr, MSVC requires these to be pointers.
+    GetGlobal* global;
+    GetLocal* local;
+    GetLifetime* lifetime;
   };
 
   //  LocalLifetime
@@ -245,7 +246,7 @@
   //  ensure this invariant.
 
   FOLLY_ERASE_NOINLINE static void mutate_slow(Signed v) noexcept {
-    static constexpr Arg arg{global, local, lifetime};
+    static constexpr Arg arg{&global, &local, &lifetime};
     mutate_slow(v, arg);
   }
 
diff --git a/folly/folly/concurrency/ThreadCachedSynchronized.h b/folly/folly/concurrency/ThreadCachedSynchronized.h
--- a/folly/folly/concurrency/ThreadCachedSynchronized.h
+++ b/folly/folly/concurrency/ThreadCachedSynchronized.h
@@ -121,7 +121,7 @@
       : truth_{static_cast<A&&>(a)...} {}
 
   template <typename A, if_<std::is_assignable<value_type&, A>{}> = 0>
-  thread_cached_synchronized& operator=(A&& a) noexcept(false) {
+  thread_cached_synchronized& operator=(A && a) noexcept(false) {
     store(static_cast<A&&>(a));
     return *this;
   }
diff --git a/folly/folly/concurrency/container/FlatCombiningPriorityQueue.h b/folly/folly/concurrency/container/FlatCombiningPriorityQueue.h
--- a/folly/folly/concurrency/container/FlatCombiningPriorityQueue.h
+++ b/folly/folly/concurrency/container/FlatCombiningPriorityQueue.h
@@ -284,8 +284,7 @@
     typename T,
     typename PriorityQueue,
     typename Mutex,
-    template <typename>
-    class Atom>
+    template <typename> class Atom>
 template <typename Clock, typename Duration>
 inline bool
 FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_push_impl(
@@ -341,8 +340,7 @@
     typename T,
     typename PriorityQueue,
     typename Mutex,
-    template <typename>
-    class Atom>
+    template <typename> class Atom>
 template <typename Clock, typename Duration>
 inline bool
 FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_pop_impl(
@@ -387,8 +385,7 @@
     typename T,
     typename PriorityQueue,
     typename Mutex,
-    template <typename>
-    class Atom>
+    template <typename> class Atom>
 template <typename Clock, typename Duration>
 inline bool
 FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_peek_impl(
diff --git a/folly/folly/concurrency/container/LockFreeRingBuffer.h b/folly/folly/concurrency/container/LockFreeRingBuffer.h
--- a/folly/folly/concurrency/container/LockFreeRingBuffer.h
+++ b/folly/folly/concurrency/container/LockFreeRingBuffer.h
@@ -34,10 +34,8 @@
 
 template <
     typename T,
-    template <typename>
-    class Atom,
-    template <typename>
-    class Storage>
+    template <typename> class Atom,
+    template <typename> class Storage>
 class RingBufferSlot;
 template <typename T>
 class RingBufferTrivialStorage;
@@ -201,10 +199,8 @@
 namespace detail {
 template <
     typename T,
-    template <typename>
-    class Atom,
-    template <typename>
-    class Storage>
+    template <typename> class Atom,
+    template <typename> class Storage>
 class RingBufferSlot {
  public:
   explicit RingBufferSlot() noexcept {}
diff --git a/folly/folly/concurrency/container/RelaxedConcurrentPriorityQueue.h b/folly/folly/concurrency/container/RelaxedConcurrentPriorityQueue.h
--- a/folly/folly/concurrency/container/RelaxedConcurrentPriorityQueue.h
+++ b/folly/folly/concurrency/container/RelaxedConcurrentPriorityQueue.h
@@ -867,8 +867,10 @@
     int num = std::min(rsize, (uint32_t)PopBatch);
     for (int i = num - 1; i >= 0; i--) {
       // wait until this block is empty
-      while (shared_buffer_[i].pnode.load(std::memory_order_relaxed) != nullptr)
+      while (
+          shared_buffer_[i].pnode.load(std::memory_order_relaxed) != nullptr) {
         ;
+      }
       shared_buffer_[i].pnode.store(head, std::memory_order_relaxed);
       head = head->next;
     }
@@ -1078,7 +1080,7 @@
       const size_t& curticket,
       const std::chrono::time_point<Clock, Duration>& deadline,
       const folly::WaitOptions& opt = wait_options()) {
-    return folly::detail::spin_pause_until(deadline, opt, [=] {
+    return folly::detail::spin_pause_until(deadline, opt, [=, this] {
              return futexIsReady(curticket);
            }) == folly::detail::spin_result::success;
   }
@@ -1143,7 +1145,7 @@
       const std::chrono::time_point<Clock, Duration>& deadline,
       const folly::WaitOptions& opt = wait_options()) {
     // Fast path, by quick check the status
-    switch (folly::detail::spin_pause_until(deadline, opt, [=] {
+    switch (folly::detail::spin_pause_until(deadline, opt, [=, this] {
       return !isEmpty();
     })) {
       case folly::detail::spin_result::success:
@@ -1156,7 +1158,7 @@
 
     // Spinning strategy
     while (true) {
-      auto res = folly::detail::spin_yield_until(deadline, [=] {
+      auto res = folly::detail::spin_yield_until(deadline, [=, this] {
         return !isEmpty();
       });
       if (res == folly::detail::spin_result::success) {
diff --git a/folly/folly/concurrency/container/atomic_grow_array.h b/folly/folly/concurrency/container/atomic_grow_array.h
--- a/folly/folly/concurrency/container/atomic_grow_array.h
+++ b/folly/folly/concurrency/container/atomic_grow_array.h
@@ -35,8 +35,11 @@
 /// atomic_grow_array_policy_default
 ///
 /// A default or example policy for use with atomic_grow_array.
-template <typename Item>
+template <typename Item, template <typename> class Atom = std::atomic>
 struct atomic_grow_array_policy_default {
+  template <typename V>
+  using atom = Atom<V>;
+
   std::size_t grow(
       std::size_t /* const curr */, std::size_t const index) const noexcept {
     return nextPowTwo(index + 1);
@@ -73,6 +76,33 @@
 ///   capacity * sizeof(value_type) <--- array-segment elements slab
 /// Modulo allocator size-classes, of which this container makes no attempt to
 /// take advantage, and value-type alignment.
+///
+/// This is a concurrent array. These are the available operations. They are all
+/// safely concurrent with each other and they all provide the required memory
+/// orderings for safe concurrent use:
+///   operator[]
+///   size
+///   empty
+///   as_view
+///   as_ptr_span
+///
+/// The container has reference and iterator stability. The references, views,
+/// and pointer-spans returned by operator[], as_view, and as_ptr_span are valid
+/// for the lifetime of the container and will never become dangling.
+///
+/// The results of empty and size are monotonic: callers in happens-before order
+/// will only ever see nondecreasing values returned by empty or by size. These
+/// values serve as lower bounds valid for the lifetime of the container.
+///
+/// Example:
+///
+///   relaxed_atomic<size_t> count{0};
+///   atomic_grow_array<object> array;
+///
+///   object& next() { return array[count++]; }
+///
+///   template <typename func>
+///   void for_each(func f) { for (auto p : array.as_ptr_span(count)) f(*p); }
 template <
     typename Item,
     typename Policy = atomic_grow_array_policy_default<Item>>
@@ -242,6 +272,15 @@
       using type = span<const_pointer const>;
       return array_ ? type{array_->list, array_->size} : type{};
     }
+
+    span<pointer const> as_ptr_span(size_type const sz) noexcept {
+      auto ptrs = as_ptr_span();
+      return ptrs.subspan(0, std::min(ptrs.size(), sz));
+    }
+    span<const_pointer const> as_ptr_span(size_type const sz) const noexcept {
+      auto ptrs = as_ptr_span();
+      return ptrs.subspan(0, std::min(ptrs.size(), sz));
+    }
   };
 
  public:
@@ -449,6 +488,12 @@
   const_pointer_span as_ptr_span() const noexcept {
     return as_view().as_ptr_span();
   }
+  pointer_span as_ptr_span(size_type const sz) noexcept {
+    return as_view().as_ptr_span(sz);
+  }
+  const_pointer_span as_ptr_span(size_type const sz) const noexcept {
+    return as_view().as_ptr_span(sz);
+  }
 
  private:
   static constexpr auto mo_acquire = std::memory_order_acquire;
@@ -522,7 +567,8 @@
     assert(size > base);
     array* curr = static_cast<array*>(
         operator_new(array_size(size, base), std::align_val_t{array_align()}));
-    auto rollback = folly::makeGuard([&] { del_array(curr); });
+    auto rollback =
+        folly::makeGuard(std::bind(&atomic_grow_array::del_array, this, curr));
     curr->size = size;
     curr->next = next;
     auto const slab = array_slab(curr);
@@ -582,8 +628,8 @@
     }
   }
 
-  std::atomic<size_type> size_{0};
-  std::atomic<array*> array_{nullptr};
+  typename Policy::template atom<size_type> size_{0};
+  typename Policy::template atom<array*> array_{nullptr};
 };
 
 } // namespace folly
diff --git a/folly/folly/concurrency/detail/AtomicSharedPtr-detail.h b/folly/folly/concurrency/detail/AtomicSharedPtr-detail.h
--- a/folly/folly/concurrency/detail/AtomicSharedPtr-detail.h
+++ b/folly/folly/concurrency/detail/AtomicSharedPtr-detail.h
@@ -41,8 +41,8 @@
   static std::shared_ptr<T> make_ptr(Args&&... args) {
     return std::make_shared<T>(std::forward<Args...>(args...));
   }
-  typedef std::__shared_count<std::_S_atomic> shared_count;
-  typedef std::_Sp_counted_base<std::_S_atomic> counted_base;
+  using shared_count = std::__shared_count<std::_S_atomic>;
+  using counted_base = std::_Sp_counted_base<std::_S_atomic>;
   template <typename T>
   using CountedPtr = std::shared_ptr<T>;
 
@@ -67,38 +67,38 @@
  private:
   /* Accessors for private members using explicit template instantiation */
   struct access_shared_ptr {
-    typedef shared_count std::__shared_ptr<const void, std::_S_atomic>::*type;
+    using type = shared_count std::__shared_ptr<const void, std::_S_atomic>::*;
     friend type fieldPtr(access_shared_ptr);
   };
 
   struct access_base {
-    typedef counted_base* shared_count::*type;
+    using type = counted_base* shared_count::*;
     friend type fieldPtr(access_base);
   };
 
   struct access_use_count {
-    typedef _Atomic_word counted_base::*type;
+    using type = _Atomic_word counted_base::*;
     friend type fieldPtr(access_use_count);
   };
 
   struct access_weak_count {
-    typedef _Atomic_word counted_base::*type;
+    using type = _Atomic_word counted_base::*;
     friend type fieldPtr(access_weak_count);
   };
 
   struct access_counted_ptr_ptr {
-    typedef const void* std::_Sp_counted_ptr<const void*, std::_S_atomic>::*
-        type;
+    using type =
+        const void* std::_Sp_counted_ptr<const void*, std::_S_atomic>::*;
     friend type fieldPtr(access_counted_ptr_ptr);
   };
 
   struct access_shared_ptr_ptr {
-    typedef const void* std::__shared_ptr<const void, std::_S_atomic>::*type;
+    using type = const void* std::__shared_ptr<const void, std::_S_atomic>::*;
     friend type fieldPtr(access_shared_ptr_ptr);
   };
 
   struct access_refcount {
-    typedef shared_count std::__shared_ptr<const void, std::_S_atomic>::*type;
+    using type = shared_count std::__shared_ptr<const void, std::_S_atomic>::*;
     friend type fieldPtr(access_refcount);
   };
 
diff --git a/folly/folly/concurrency/detail/ConcurrentHashMap-detail.h b/folly/folly/concurrency/detail/ConcurrentHashMap-detail.h
--- a/folly/folly/concurrency/detail/ConcurrentHashMap-detail.h
+++ b/folly/folly/concurrency/detail/ConcurrentHashMap-detail.h
@@ -18,16 +18,18 @@
 
 #include <algorithm>
 #include <atomic>
+#include <memory>
 #include <mutex>
 #include <new>
 
 #include <folly/ScopeGuard.h>
 #include <folly/container/HeterogeneousAccess.h>
 #include <folly/container/detail/F14Mask.h>
+#include <folly/lang/Align.h>
 #include <folly/lang/Exception.h>
 #include <folly/synchronization/Hazptr.h>
 
-#ifdef __aarch64__
+#if FOLLY_AARCH64 && FOLLY_F14_CRC_INTRINSIC_AVAILABLE
 #include <arm_acle.h>
 #include <arm_neon.h>
 #elif FOLLY_SSE_PREREQ(4, 2) && !FOLLY_MOBILE
@@ -40,6 +42,44 @@
 
 namespace concurrenthashmap {
 
+template <typename Class, typename ValueType, typename Allocator>
+class alignas(alignof(ValueType)) AlignedStorage {
+  struct alignas(alignof(Class)) Dummy {};
+
+  using DummyAllocator =
+      typename std::allocator_traits<Allocator>::template rebind_alloc<Dummy>;
+  using DummyAllocTraits = std::allocator_traits<DummyAllocator>;
+
+  static size_t calculateNeededDummyCount(size_t count) {
+    const size_t sz = sizeof(Class) + sizeof(ValueType) * count;
+    static_assert(alignof(Dummy) % alignof(ValueType) == 0);
+    static_assert(alignof(Dummy) % alignof(Class) == 0);
+    const size_t aligned_sz = align_ceil(sz, alignof(Dummy));
+    return aligned_sz / sizeof(Dummy);
+  }
+
+ public:
+  ValueType* array() noexcept {
+    return reinterpret_cast<ValueType*>(static_cast<Class*>(this) + 1);
+  }
+
+ protected:
+  static Class* allocate(size_t count) {
+    auto allocator = DummyAllocator();
+    auto* buf =
+        DummyAllocTraits::allocate(allocator, calculateNeededDummyCount(count));
+    return reinterpret_cast<Class*>(buf);
+  }
+
+  void deallocate(size_t count) {
+    auto allocator = DummyAllocator();
+    DummyAllocTraits::deallocate(
+        allocator,
+        reinterpret_cast<Dummy*>(static_cast<Class*>(this)),
+        calculateNeededDummyCount(count));
+  }
+};
+
 enum class InsertType {
   DOES_NOT_EXIST, // insert/emplace operations.  If key exists, return false.
   MUST_EXIST, // assign operations.  If key does not exist, return false.
@@ -55,12 +95,11 @@
     typename KeyType,
     typename ValueType,
     typename Allocator,
-    template <typename>
-    class Atom,
+    template <typename> class Atom,
     typename Enabled = void>
 class ValueHolder {
  public:
-  typedef std::pair<const KeyType, ValueType> value_type;
+  using value_type = std::pair<const KeyType, ValueType>;
 
   explicit ValueHolder(const ValueHolder& other) : item_(other.item_) {}
 
@@ -83,8 +122,7 @@
     typename KeyType,
     typename ValueType,
     typename Allocator,
-    template <typename>
-    class Atom>
+    template <typename> class Atom>
 class ValueHolder<
     KeyType,
     ValueType,
@@ -93,7 +131,7 @@
     std::enable_if_t<
         !std::is_nothrow_copy_constructible<ValueType>::value ||
         !std::is_nothrow_copy_constructible<KeyType>::value>> {
-  typedef std::pair<const KeyType, ValueType> value_type;
+  using value_type = std::pair<const KeyType, ValueType>;
 
   struct CountedItem {
     value_type kv_;
@@ -123,6 +161,13 @@
   }; // CountedItem
   // Back to ValueHolder specialization
 
+  struct DeallocateFn { // Avoids dependent lambdas in function templates
+    ValueHolder& self;
+    void operator()() const noexcept {
+      Allocator().deallocate((uint8_t*)self.item_, sizeof(CountedItem));
+    }
+  };
+
   CountedItem* item_; // Link to unique key-value item.
 
  public:
@@ -137,9 +182,7 @@
   template <typename Arg, typename... Args>
   ValueHolder(std::piecewise_construct_t, Arg&& k, Args&&... args) {
     item_ = (CountedItem*)Allocator().allocate(sizeof(CountedItem));
-    auto g = makeGuard([&] {
-      Allocator().deallocate((uint8_t*)item_, sizeof(CountedItem));
-    });
+    auto g = makeGuard(DeallocateFn{*this});
     new (item_) CountedItem(
         std::piecewise_construct,
         std::forward<Arg>(k),
@@ -163,6 +206,13 @@
 
 template <typename Node, typename Allocator>
 struct AllocNodeGuard : NonCopyableNonMovable {
+  struct DeallocateFn { // Avoids dependent lambdas in function templates
+    AllocNodeGuard& self;
+    void operator()() const noexcept {
+      self.alloc.deallocate((uint8_t*)self.node, sizeof(Node));
+    }
+  };
+
   Allocator alloc;
   Node* node{};
 
@@ -171,10 +221,8 @@
 
   template <typename... Arg>
   explicit AllocNodeGuard(Allocator alloc_, Arg&&... arg)
-      : alloc{std::move(alloc_)}, node{(Node*)alloc_.allocate(sizeof(Node))} {
-    auto guard = makeGuard([&] {
-      alloc.deallocate((uint8_t*)node, sizeof(Node));
-    });
+      : alloc{std::move(alloc_)}, node{(Node*)alloc.allocate(sizeof(Node))} {
+    auto guard = makeGuard(DeallocateFn{*this});
     new (node) Node(std::forward<Arg>(arg)...);
     guard.dismiss();
   }
@@ -228,7 +276,7 @@
           Atom,
           concurrenthashmap::HazptrDeleter<Allocator>> {
  public:
-  typedef std::pair<const KeyType, ValueType> value_type;
+  using value_type = std::pair<const KeyType, ValueType>;
 
   explicit NodeT(hazptr_obj_cohort<Atom>* cohort, NodeT* other)
       : item_(other->item_) {
@@ -287,7 +335,7 @@
   // perfectly balanced, reserve(size) will still work without
   // rehashing.
   static constexpr float kDefaultLoadFactor = 1.05f;
-  typedef std::pair<const KeyType, ValueType> value_type;
+  using value_type = std::pair<const KeyType, ValueType>;
 
   using Node =
       concurrenthashmap::bucket::NodeT<KeyType, ValueType, Allocator, Atom>;
@@ -380,7 +428,7 @@
     load_factor_nodes_ =
         to_integral(static_cast<float>(bucket_count) * load_factor_);
     for (size_t i = 0; i < oldcount; i++) {
-      auto bucket = &buckets->buckets_[i]();
+      auto bucket = &buckets->array()[i]();
       auto node = bucket->load(std::memory_order_relaxed);
       if (!node) {
         continue;
@@ -404,14 +452,14 @@
       }
       // Set longest last run in new bucket, incrementing the refcount.
       lastrun->acquire_link(); // defined in hazptr_obj_base_linked
-      newbuckets->buckets_[lastidx]().store(lastrun, std::memory_order_relaxed);
+      newbuckets->array()[lastidx]().store(lastrun, std::memory_order_relaxed);
       // Clone remaining nodes
       for (; node != lastrun;
            node = node->next_.load(std::memory_order_relaxed)) {
         auto newnode = (Node*)Allocator().allocate(sizeof(Node));
         new (newnode) Node(cohort, node);
         auto k = getIdx(bucket_count, HashFn()(node->getItem().first));
-        auto prevhead = &newbuckets->buckets_[k]();
+        auto prevhead = &newbuckets->array()[k]();
         newnode->next_.store(prevhead->load(std::memory_order_relaxed));
         prevhead->store(newnode, std::memory_order_relaxed);
       }
@@ -435,7 +483,7 @@
     getBucketsAndCount(bcount, buckets, res.hazptrs_[0]);
 
     auto idx = getIdx(bcount, h);
-    auto prev = &buckets->buckets_[idx]();
+    auto prev = &buckets->array()[idx]();
     auto node = hazcurr.protect(*prev);
     while (node) {
       if (KeyEqual()(k, node->getItem().first)) {
@@ -458,7 +506,7 @@
       auto buckets = buckets_.load(std::memory_order_relaxed);
       DCHECK(buckets); // Use-after-destruction by user.
       auto idx = getIdx(bcount, h);
-      auto head = &buckets->buckets_[idx]();
+      auto head = &buckets->array()[idx]();
       node = head->load(std::memory_order_relaxed);
       Node* prev = nullptr;
       while (node) {
@@ -537,45 +585,41 @@
   Iterator cend() { return Iterator(nullptr); }
 
  private:
-  // Could be optimized to avoid an extra pointer dereference by
-  // allocating buckets_ at the same time.
+  using BucketRoot = hazptr_root<Node, Atom>;
   class Buckets
       : public hazptr_obj_base<
             Buckets,
             Atom,
-            concurrenthashmap::HazptrTableDeleter> {
-    using BucketRoot = hazptr_root<Node, Atom>;
-
-    Buckets() {}
-    ~Buckets() {}
+            concurrenthashmap::HazptrTableDeleter>,
+        public AlignedStorage<Buckets, BucketRoot, Allocator> {
+    Buckets() = default;
+    ~Buckets() = default;
 
    public:
     static Buckets* create(size_t count, hazptr_obj_cohort<Atom>* cohort) {
-      auto buf =
-          Allocator().allocate(sizeof(Buckets) + sizeof(BucketRoot) * count);
-      auto buckets = new (buf) Buckets();
+      auto* buf = Buckets::allocate(count);
+      auto* buckets = new (buf) Buckets();
       DCHECK(cohort);
       buckets->set_cohort_tag(cohort); // defined in hazptr_obj
       for (size_t i = 0; i < count; i++) {
-        new (&buckets->buckets_[i]) BucketRoot;
+        new (&buckets->array()[i]) BucketRoot;
       }
       return buckets;
     }
 
     void destroy(size_t count) {
       for (size_t i = 0; i < count; i++) {
-        buckets_[i].~BucketRoot();
+        Buckets::array()[i].~BucketRoot();
       }
       this->~Buckets();
-      Allocator().deallocate(
-          (uint8_t*)this, sizeof(BucketRoot) * count + sizeof(*this));
+      Buckets::deallocate(count);
     }
 
     void unlink_and_reclaim_nodes(size_t count) {
       for (size_t i = 0; i < count; i++) {
-        auto node = buckets_[i]().load(std::memory_order_relaxed);
+        auto node = Buckets::array()[i]().load(std::memory_order_relaxed);
         if (node) {
-          buckets_[i]().store(nullptr, std::memory_order_relaxed);
+          Buckets::array()[i]().store(nullptr, std::memory_order_relaxed);
           while (node) {
             auto next = node->next_.load(std::memory_order_relaxed);
             if (next) {
@@ -587,8 +631,6 @@
         }
       }
     }
-
-    BucketRoot buckets_[0];
   };
 
  public:
@@ -634,7 +676,7 @@
           break;
         }
         DCHECK(buckets_);
-        node_ = hazptrs_[1].protect(buckets_->buckets_[idx_]());
+        node_ = hazptrs_[1].protect(buckets_->array()[idx_]());
         if (node_) {
           break;
         }
@@ -727,7 +769,7 @@
 
     DCHECK(buckets) << "Use-after-destruction by user.";
     auto idx = getIdx(bcount, h);
-    auto head = &buckets->buckets_[idx]();
+    auto head = &buckets->array()[idx]();
     auto node = head->load(std::memory_order_relaxed);
     auto headnode = node;
     auto prev = head;
@@ -794,7 +836,7 @@
       bcount <<= 1;
       hazbuckets.reset_protection(buckets);
       idx = getIdx(bcount, h);
-      head = &buckets->buckets_[idx]();
+      head = &buckets->array()[idx]();
       headnode = head->load(std::memory_order_relaxed);
     }
 
@@ -830,7 +872,9 @@
 
 } // namespace bucket
 
-#if (FOLLY_SSE_PREREQ(4, 2) || FOLLY_AARCH64) && \
+#if (                                                        \
+    FOLLY_SSE_PREREQ(4, 2) ||                                \
+    (FOLLY_AARCH64 && FOLLY_F14_CRC_INTRINSIC_AVAILABLE)) && \
     FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
 
 namespace simd {
@@ -855,7 +899,7 @@
           Atom,
           HazptrDeleter<Allocator>> {
  public:
-  typedef std::pair<const KeyType, ValueType> value_type;
+  using value_type = std::pair<const KeyType, ValueType>;
 
   template <typename Arg, typename... Args>
   NodeT(hazptr_obj_cohort<Atom>* cohort, Arg&& k, Args&&... args)
@@ -1082,35 +1126,36 @@
     }
   };
 
-  class Chunks : public hazptr_obj_base<Chunks, Atom, HazptrTableDeleter> {
-    Chunks() {}
-    ~Chunks() {}
+  class Chunks
+      : public hazptr_obj_base<Chunks, Atom, HazptrTableDeleter>,
+        public AlignedStorage<Chunks, Chunk, Allocator> {
+    Chunks() = default;
+    ~Chunks() = default;
 
    public:
     static Chunks* create(size_t count, hazptr_obj_cohort<Atom>* cohort) {
-      auto buf = Allocator().allocate(sizeof(Chunks) + sizeof(Chunk) * count);
-      auto chunks = new (buf) Chunks();
+      auto* buf = Chunks::allocate(count);
+      auto* chunks = new (buf) Chunks();
       DCHECK(cohort);
       chunks->set_cohort_tag(cohort); // defined in hazptr_obj
       for (size_t i = 0; i < count; i++) {
-        new (&chunks->chunks_[i]) Chunk;
-        chunks->chunks_[i].clear();
+        new (&chunks->array()[i]) Chunk;
+        chunks->array()[i].clear();
       }
       return chunks;
     }
 
     void destroy(size_t count) {
       for (size_t i = 0; i < count; i++) {
-        chunks_[i].~Chunk();
+        Chunks::array()[i].~Chunk();
       }
       this->~Chunks();
-      Allocator().deallocate(
-          (uint8_t*)this, sizeof(Chunk) * count + sizeof(*this));
+      Chunks::deallocate(count);
     }
 
     void reclaim_nodes(size_t count) {
       for (size_t i = 0; i < count; i++) {
-        Chunk& chunk = chunks_[i];
+        Chunk& chunk = Chunks::array()[i];
         auto occupied = chunk.occupiedIter();
         while (occupied.hasNext()) {
           auto idx = occupied.next();
@@ -1126,18 +1171,15 @@
 
     Chunk* getChunk(size_t index, size_t ccount) {
       DCHECK(isPowTwo(ccount));
-      return &chunks_[index & (ccount - 1)];
+      return &Chunks::array()[index & (ccount - 1)];
     }
-
-   private:
-    Chunk chunks_[0];
   };
 
  public:
   static constexpr float kDefaultLoadFactor =
       Chunk::kDesiredCapacity / (float)Chunk::kCapacity;
 
-  typedef std::pair<const KeyType, ValueType> value_type;
+  using value_type = std::pair<const KeyType, ValueType>;
 
   using InsertType = concurrenthashmap::InsertType;
 
@@ -1732,6 +1774,14 @@
 
 #endif // FOLLY_SSE_PREREQ(4, 2) && FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
 
+template <typename ValueType>
+struct EqualTo1 {
+  const ValueType& rhs;
+  bool operator()(const ValueType& lhs) const { return lhs == rhs; }
+};
+template <typename ValueType>
+EqualTo1(const ValueType&) -> EqualTo1<ValueType>;
+
 } // namespace concurrenthashmap
 
 /* A Segment is a single shard of the ConcurrentHashMap.
@@ -1773,10 +1823,8 @@
         typename,
         typename,
         typename,
-        template <typename>
-        class,
-        class>
-    class Impl = concurrenthashmap::bucket::BucketTable>
+        template <typename> class,
+        class> class Impl = concurrenthashmap::bucket::BucketTable>
 class alignas(64) ConcurrentHashMapSegment {
   using ImplT = Impl<
       KeyType,
@@ -1789,10 +1837,10 @@
       Mutex>;
 
  public:
-  typedef KeyType key_type;
-  typedef ValueType mapped_type;
-  typedef std::pair<const KeyType, ValueType> value_type;
-  typedef std::size_t size_type;
+  using key_type = KeyType;
+  using mapped_type = ValueType;
+  using value_type = std::pair<const KeyType, ValueType>;
+  using size_type = std::size_t;
 
   using InsertType = concurrenthashmap::InsertType;
   using Iterator = typename ImplT::Iterator;
@@ -1828,7 +1876,7 @@
         h,
         g.node->getItem().first,
         InsertType::DOES_NOT_EXIST,
-        [](const ValueType&) { return false; },
+        variadic_constant_of<false>,
         g.node);
     if (res) {
       g.dismiss();
@@ -1845,7 +1893,7 @@
         h,
         std::forward<Key>(k),
         InsertType::DOES_NOT_EXIST,
-        [](const ValueType&) { return false; },
+        variadic_constant_of<false>,
         std::forward<Key>(k),
         std::forward<Args>(args)...);
   }
@@ -1857,7 +1905,7 @@
         h,
         k,
         InsertType::DOES_NOT_EXIST,
-        [](const ValueType&) { return false; },
+        variadic_constant_of<false>,
         node);
   }
 
@@ -1870,7 +1918,7 @@
         h,
         g.node->getItem().first,
         InsertType::ANY,
-        [](const ValueType&) { return false; },
+        variadic_constant_of<false>,
         g.node);
     if (res) {
       g.dismiss();
@@ -1908,7 +1956,7 @@
         h,
         g.node->getItem().first,
         InsertType::MUST_EXIST,
-        [](const ValueType&) { return false; },
+        variadic_constant_of<false>,
         g.node);
     if (res) {
       g.dismiss();
@@ -1949,7 +1997,7 @@
         h,
         std::forward<Key>(k),
         std::forward<Value>(desired),
-        [&expected](const ValueType& v) { return v == expected; });
+        concurrenthashmap::EqualTo1{expected});
   }
 
   template <typename MatchFunc, typename K, typename... Args>
@@ -1988,9 +2036,7 @@
   // Listed separately because we need a prev pointer.
   template <typename K>
   size_type erase(size_t h, const K& key) {
-    return erase_internal(h, key, nullptr, [](const ValueType&) {
-      return true;
-    });
+    return erase_internal(h, key, nullptr, variadic_constant_of<true>);
   }
 
   template <typename K, typename Predicate>
@@ -2011,7 +2057,7 @@
   // This is a small departure from standard stl containers: erase may
   // throw if hash or key_eq functions throw.
   void erase(Iterator& res, Iterator& pos, size_t h) {
-    erase_internal(h, pos->first, &res, [](const ValueType&) { return true; });
+    erase_internal(h, pos->first, &res, variadic_constant_of<true>);
     // Invalidate the iterator.
     pos = cend();
   }
diff --git a/folly/folly/concurrency/memory/PrimaryPtr.h b/folly/folly/concurrency/memory/PrimaryPtr.h
--- a/folly/folly/concurrency/memory/PrimaryPtr.h
+++ b/folly/folly/concurrency/memory/PrimaryPtr.h
@@ -187,7 +187,7 @@
     return nullptr;
   }
 
-  // Waits until all the refereces obtained via lock() are released. Then
+  // Waits until all the references obtained via lock() are released. Then
   // destroys the object in the current thread.
   // Can not be called concurrently with set().
   void join() {
@@ -197,7 +197,7 @@
     this->cleanup().get();
   }
 
-  // Returns: a SemiFuture that waits until all the refereces obtained via
+  // Returns: a SemiFuture that waits until all the references obtained via
   // lock() are released. Then destroys the object on the Executor provided to
   // the SemiFuture.
   //
@@ -211,7 +211,7 @@
         .deferValue([this](folly::Unit) {
           if (!this->outerPtrShared_) {
             LOG(FATAL)
-                << "Cleanup already run - lock() was previouly disabled.";
+                << "Cleanup already run - lock() was previously disabled.";
           }
           this->outerPtrShared_.reset();
           return std::move(this->unreferenced_);
diff --git a/folly/folly/container/CollectionUtil.h b/folly/folly/container/CollectionUtil.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/container/CollectionUtil.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <concepts>
+
+namespace folly {
+
+namespace detail {
+template <typename T, typename K>
+concept HasContains = requires(T& t, const K& k) {
+  { t.contains(k) };
+};
+
+template <typename T, typename K>
+concept HasFind = requires(T& t, const K& k) {
+  { t.find(k) } -> std::convertible_to<typename T::const_iterator>;
+};
+} // namespace detail
+/**
+ * This function check whether container contains given value.
+ * Can be used for non-associative containers.
+ * Notes:
+ * Runtime = O(n)
+ * Work with vector, map.
+ */
+template <class C, class V = typename C::value_type>
+  requires(!detail::HasContains<C, V> && !detail::HasFind<C, V>)
+bool contains(const C& container, const V& value) {
+  const auto e = std::end(container);
+  return std::find(std::begin(container), e, value) != e;
+}
+
+/**
+ * This function checks whether container contains given key.
+ * Use container specific .contains() implementation if available,
+ * otherwise uses .find() implementation if available, otherwise
+ * fallback to O(n) std::find() implementation.
+ */
+template <class C, class K = typename C::key_type>
+bool contains(const C& container, const K& key) {
+  if constexpr (detail::HasContains<C, K>) {
+    return container.contains(key);
+  } else if constexpr (detail::HasFind<C, K>) {
+    return container.find(key) != container.end();
+  } else {
+    // Fallback: use generic and possibly slower std::find otherwise.
+    return contains(container, key);
+  }
+}
+
+} // namespace folly
diff --git a/folly/folly/container/EvictingCacheMap.h b/folly/folly/container/EvictingCacheMap.h
--- a/folly/folly/container/EvictingCacheMap.h
+++ b/folly/folly/container/EvictingCacheMap.h
@@ -49,7 +49,7 @@
  * NOTE: maxSize==0 is a special case that disables automatic evictions.
  * prune() can be used for manually trimming down the number of entries.
  *
- * Implementaion: Maintains a doubly linked list (`lru_`) of entry nodes in
+ * Implementation: Maintains a doubly linked list (`lru_`) of entry nodes in
  * LRU order, which are also connected to hash table index (`index_`). The
  * access order is maintained on the list by moving an element to the front
  * of list on a get, and adding to the front on insert. Assuming quality
@@ -168,7 +168,7 @@
         keyEqual_(keyEqual),
         index_(maxSize + /*transient*/ 1, keyHash_, keyEqual_),
         maxSize_(maxSize),
-        clearSize_(clearSize) {}
+        clearSize_(clampClearSize(clearSize)) {}
 
   EvictingCacheMap(const EvictingCacheMap&) = delete;
   EvictingCacheMap& operator=(const EvictingCacheMap&) = delete;
@@ -181,7 +181,7 @@
    * Adjust the max size of EvictingCacheMap, evicting as needed to ensure the
    * new max is not exceeded.
    *
-   * Calling this function with an arugment of 0 removes the limit on the cache
+   * Calling this function with an argument of 0 removes the limit on the cache
    * size and elements are not evicted unless clients explicitly call prune.
    *
    * @param maxSize new maximum size of the cache map.
@@ -197,7 +197,9 @@
 
   std::size_t getMaxSize() const { return maxSize_; }
 
-  void setClearSize(std::size_t clearSize) { clearSize_ = clearSize; }
+  void setClearSize(std::size_t clearSize) {
+    clearSize_ = clampClearSize(clearSize);
+  }
 
   /**
    * Check for existence of a specific key in the map.  This operation has
@@ -735,6 +737,12 @@
     } else {
       return nullptr;
     }
+  }
+
+  // A zero clear size doesn't make sense. If you want to disable clearing, set
+  // maxSize to 0.
+  static std::size_t clampClearSize(std::size_t clearSize) {
+    return std::max(clearSize, std::size_t{1});
   }
 
   PruneHookCall pruneHook_;
diff --git a/folly/folly/container/F14Map.h b/folly/folly/container/F14Map.h
--- a/folly/folly/container/F14Map.h
+++ b/folly/folly/container/F14Map.h
@@ -1154,13 +1154,12 @@
     typename Alloc,
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireAllocator<Alloc>>
-F14ValueMap(InputIt, InputIt, std::size_t, Alloc)
-    -> F14ValueMap<
-        iterator_key_type_t<InputIt>,
-        iterator_mapped_type_t<InputIt>,
-        f14::DefaultHasher<iterator_key_type_t<InputIt>>,
-        f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
-        Alloc>;
+F14ValueMap(InputIt, InputIt, std::size_t, Alloc) -> F14ValueMap<
+    iterator_key_type_t<InputIt>,
+    iterator_mapped_type_t<InputIt>,
+    f14::DefaultHasher<iterator_key_type_t<InputIt>>,
+    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename InputIt,
@@ -1169,13 +1168,12 @@
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireNotAllocator<Hasher>,
     typename = detail::RequireAllocator<Alloc>>
-F14ValueMap(InputIt, InputIt, std::size_t, Hasher, Alloc)
-    -> F14ValueMap<
-        iterator_key_type_t<InputIt>,
-        iterator_mapped_type_t<InputIt>,
-        Hasher,
-        f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
-        Alloc>;
+F14ValueMap(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14ValueMap<
+    iterator_key_type_t<InputIt>,
+    iterator_mapped_type_t<InputIt>,
+    Hasher,
+    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename Key,
@@ -1304,13 +1302,12 @@
     typename Alloc,
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireAllocator<Alloc>>
-F14NodeMap(InputIt, InputIt, std::size_t, Alloc)
-    -> F14NodeMap<
-        iterator_key_type_t<InputIt>,
-        iterator_mapped_type_t<InputIt>,
-        f14::DefaultHasher<iterator_key_type_t<InputIt>>,
-        f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
-        Alloc>;
+F14NodeMap(InputIt, InputIt, std::size_t, Alloc) -> F14NodeMap<
+    iterator_key_type_t<InputIt>,
+    iterator_mapped_type_t<InputIt>,
+    f14::DefaultHasher<iterator_key_type_t<InputIt>>,
+    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename InputIt,
@@ -1319,13 +1316,12 @@
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireNotAllocator<Hasher>,
     typename = detail::RequireAllocator<Alloc>>
-F14NodeMap(InputIt, InputIt, std::size_t, Hasher, Alloc)
-    -> F14NodeMap<
-        iterator_key_type_t<InputIt>,
-        iterator_mapped_type_t<InputIt>,
-        Hasher,
-        f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
-        Alloc>;
+F14NodeMap(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14NodeMap<
+    iterator_key_type_t<InputIt>,
+    iterator_mapped_type_t<InputIt>,
+    Hasher,
+    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename Key,
@@ -1715,13 +1711,12 @@
     typename Alloc,
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireAllocator<Alloc>>
-F14VectorMap(InputIt, InputIt, std::size_t, Alloc)
-    -> F14VectorMap<
-        iterator_key_type_t<InputIt>,
-        iterator_mapped_type_t<InputIt>,
-        f14::DefaultHasher<iterator_key_type_t<InputIt>>,
-        f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
-        Alloc>;
+F14VectorMap(InputIt, InputIt, std::size_t, Alloc) -> F14VectorMap<
+    iterator_key_type_t<InputIt>,
+    iterator_mapped_type_t<InputIt>,
+    f14::DefaultHasher<iterator_key_type_t<InputIt>>,
+    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename InputIt,
@@ -1730,13 +1725,12 @@
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireNotAllocator<Hasher>,
     typename = detail::RequireAllocator<Alloc>>
-F14VectorMap(InputIt, InputIt, std::size_t, Hasher, Alloc)
-    -> F14VectorMap<
-        iterator_key_type_t<InputIt>,
-        iterator_mapped_type_t<InputIt>,
-        Hasher,
-        f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
-        Alloc>;
+F14VectorMap(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14VectorMap<
+    iterator_key_type_t<InputIt>,
+    iterator_mapped_type_t<InputIt>,
+    Hasher,
+    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename Key,
@@ -1853,13 +1847,12 @@
     typename Alloc,
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireAllocator<Alloc>>
-F14FastMap(InputIt, InputIt, std::size_t, Alloc)
-    -> F14FastMap<
-        iterator_key_type_t<InputIt>,
-        iterator_mapped_type_t<InputIt>,
-        f14::DefaultHasher<iterator_key_type_t<InputIt>>,
-        f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
-        Alloc>;
+F14FastMap(InputIt, InputIt, std::size_t, Alloc) -> F14FastMap<
+    iterator_key_type_t<InputIt>,
+    iterator_mapped_type_t<InputIt>,
+    f14::DefaultHasher<iterator_key_type_t<InputIt>>,
+    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename InputIt,
@@ -1868,13 +1861,12 @@
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireNotAllocator<Hasher>,
     typename = detail::RequireAllocator<Alloc>>
-F14FastMap(InputIt, InputIt, std::size_t, Hasher, Alloc)
-    -> F14FastMap<
-        iterator_key_type_t<InputIt>,
-        iterator_mapped_type_t<InputIt>,
-        Hasher,
-        f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
-        Alloc>;
+F14FastMap(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14FastMap<
+    iterator_key_type_t<InputIt>,
+    iterator_mapped_type_t<InputIt>,
+    Hasher,
+    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename Key,
diff --git a/folly/folly/container/F14Set.h b/folly/folly/container/F14Set.h
--- a/folly/folly/container/F14Set.h
+++ b/folly/folly/container/F14Set.h
@@ -873,12 +873,11 @@
     typename Alloc,
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireAllocator<Alloc>>
-F14ValueSet(InputIt, InputIt, std::size_t, Alloc)
-    -> F14ValueSet<
-        iterator_value_type_t<InputIt>,
-        f14::DefaultHasher<iterator_value_type_t<InputIt>>,
-        f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
-        Alloc>;
+F14ValueSet(InputIt, InputIt, std::size_t, Alloc) -> F14ValueSet<
+    iterator_value_type_t<InputIt>,
+    f14::DefaultHasher<iterator_value_type_t<InputIt>>,
+    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename InputIt,
@@ -887,12 +886,11 @@
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireNotAllocator<Hasher>,
     typename = detail::RequireAllocator<Alloc>>
-F14ValueSet(InputIt, InputIt, std::size_t, Hasher, Alloc)
-    -> F14ValueSet<
-        iterator_value_type_t<InputIt>,
-        Hasher,
-        f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
-        Alloc>;
+F14ValueSet(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14ValueSet<
+    iterator_value_type_t<InputIt>,
+    Hasher,
+    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename Key,
@@ -913,12 +911,11 @@
     typename Key,
     typename Alloc,
     typename = detail::RequireAllocator<Alloc>>
-F14ValueSet(std::initializer_list<Key>, std::size_t, Alloc)
-    -> F14ValueSet<
-        Key,
-        f14::DefaultHasher<Key>,
-        f14::DefaultKeyEqual<Key>,
-        Alloc>;
+F14ValueSet(std::initializer_list<Key>, std::size_t, Alloc) -> F14ValueSet<
+    Key,
+    f14::DefaultHasher<Key>,
+    f14::DefaultKeyEqual<Key>,
+    Alloc>;
 
 template <
     typename Key,
@@ -992,12 +989,11 @@
     typename Alloc,
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireAllocator<Alloc>>
-F14NodeSet(InputIt, InputIt, std::size_t, Alloc)
-    -> F14NodeSet<
-        iterator_value_type_t<InputIt>,
-        f14::DefaultHasher<iterator_value_type_t<InputIt>>,
-        f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
-        Alloc>;
+F14NodeSet(InputIt, InputIt, std::size_t, Alloc) -> F14NodeSet<
+    iterator_value_type_t<InputIt>,
+    f14::DefaultHasher<iterator_value_type_t<InputIt>>,
+    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename InputIt,
@@ -1006,12 +1002,11 @@
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireNotAllocator<Hasher>,
     typename = detail::RequireAllocator<Alloc>>
-F14NodeSet(InputIt, InputIt, std::size_t, Hasher, Alloc)
-    -> F14NodeSet<
-        iterator_value_type_t<InputIt>,
-        Hasher,
-        f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
-        Alloc>;
+F14NodeSet(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14NodeSet<
+    iterator_value_type_t<InputIt>,
+    Hasher,
+    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename Key,
@@ -1032,12 +1027,11 @@
     typename Key,
     typename Alloc,
     typename = detail::RequireAllocator<Alloc>>
-F14NodeSet(std::initializer_list<Key>, std::size_t, Alloc)
-    -> F14NodeSet<
-        Key,
-        f14::DefaultHasher<Key>,
-        f14::DefaultKeyEqual<Key>,
-        Alloc>;
+F14NodeSet(std::initializer_list<Key>, std::size_t, Alloc) -> F14NodeSet<
+    Key,
+    f14::DefaultHasher<Key>,
+    f14::DefaultKeyEqual<Key>,
+    Alloc>;
 
 template <
     typename Key,
@@ -1312,12 +1306,11 @@
     typename Alloc,
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireAllocator<Alloc>>
-F14VectorSet(InputIt, InputIt, std::size_t, Alloc)
-    -> F14VectorSet<
-        iterator_value_type_t<InputIt>,
-        f14::DefaultHasher<iterator_value_type_t<InputIt>>,
-        f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
-        Alloc>;
+F14VectorSet(InputIt, InputIt, std::size_t, Alloc) -> F14VectorSet<
+    iterator_value_type_t<InputIt>,
+    f14::DefaultHasher<iterator_value_type_t<InputIt>>,
+    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename InputIt,
@@ -1326,12 +1319,11 @@
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireNotAllocator<Hasher>,
     typename = detail::RequireAllocator<Alloc>>
-F14VectorSet(InputIt, InputIt, std::size_t, Hasher, Alloc)
-    -> F14VectorSet<
-        iterator_value_type_t<InputIt>,
-        Hasher,
-        f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
-        Alloc>;
+F14VectorSet(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14VectorSet<
+    iterator_value_type_t<InputIt>,
+    Hasher,
+    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename Key,
@@ -1352,12 +1344,11 @@
     typename Key,
     typename Alloc,
     typename = detail::RequireAllocator<Alloc>>
-F14VectorSet(std::initializer_list<Key>, std::size_t, Alloc)
-    -> F14VectorSet<
-        Key,
-        f14::DefaultHasher<Key>,
-        f14::DefaultKeyEqual<Key>,
-        Alloc>;
+F14VectorSet(std::initializer_list<Key>, std::size_t, Alloc) -> F14VectorSet<
+    Key,
+    f14::DefaultHasher<Key>,
+    f14::DefaultKeyEqual<Key>,
+    Alloc>;
 
 template <
     typename Key,
@@ -1417,12 +1408,11 @@
     typename Alloc,
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireAllocator<Alloc>>
-F14FastSet(InputIt, InputIt, std::size_t, Alloc)
-    -> F14FastSet<
-        iterator_value_type_t<InputIt>,
-        f14::DefaultHasher<iterator_value_type_t<InputIt>>,
-        f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
-        Alloc>;
+F14FastSet(InputIt, InputIt, std::size_t, Alloc) -> F14FastSet<
+    iterator_value_type_t<InputIt>,
+    f14::DefaultHasher<iterator_value_type_t<InputIt>>,
+    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename InputIt,
@@ -1431,12 +1421,11 @@
     typename = detail::RequireInputIterator<InputIt>,
     typename = detail::RequireNotAllocator<Hasher>,
     typename = detail::RequireAllocator<Alloc>>
-F14FastSet(InputIt, InputIt, std::size_t, Hasher, Alloc)
-    -> F14FastSet<
-        iterator_value_type_t<InputIt>,
-        Hasher,
-        f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
-        Alloc>;
+F14FastSet(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14FastSet<
+    iterator_value_type_t<InputIt>,
+    Hasher,
+    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,
+    Alloc>;
 
 template <
     typename Key,
@@ -1457,12 +1446,11 @@
     typename Key,
     typename Alloc,
     typename = detail::RequireAllocator<Alloc>>
-F14FastSet(std::initializer_list<Key>, std::size_t, Alloc)
-    -> F14FastSet<
-        Key,
-        f14::DefaultHasher<Key>,
-        f14::DefaultKeyEqual<Key>,
-        Alloc>;
+F14FastSet(std::initializer_list<Key>, std::size_t, Alloc) -> F14FastSet<
+    Key,
+    f14::DefaultHasher<Key>,
+    f14::DefaultKeyEqual<Key>,
+    Alloc>;
 
 template <
     typename Key,
diff --git a/folly/folly/container/FBVector.h b/folly/folly/container/FBVector.h
--- a/folly/folly/container/FBVector.h
+++ b/folly/folly/container/FBVector.h
@@ -610,7 +610,7 @@
   //     Destroys the old data.
   //
   //  3: relocate_undo
-  //     Destoys the new data and restores the old data.
+  //     Destroys the new data and restores the old data.
   //
   // The three steps are used because there may be an exception after part 1
   //  has completed. If that is the case, then relocate_undo can nullify the
diff --git a/folly/folly/container/HeterogeneousAccess.h b/folly/folly/container/HeterogeneousAccess.h
--- a/folly/folly/container/HeterogeneousAccess.h
+++ b/folly/folly/container/HeterogeneousAccess.h
@@ -24,6 +24,7 @@
 #include <folly/Traits.h>
 #include <folly/container/HeterogeneousAccess-fwd.h>
 #include <folly/hash/Hash.h>
+#include <folly/hash/detail/RandomSeed.h>
 #include <folly/hash/rapidhash.h>
 
 namespace folly {
@@ -48,6 +49,10 @@
 // Additional specializations of HeterogeneousAccess*<T> should go in
 // the header that declares T.  Don't forget to typedef is_transparent to
 // void and folly_is_avalanching to std::true_type in the specializations.
+//
+// Hash values of folly::HeterogeneousAccessHash are not guaranteed to be
+// stable across different program runs, so do not persist hash values
+// anywhere.
 
 template <typename T, typename Enable>
 struct HeterogeneousAccessEqualTo : std::equal_to<T> {};
@@ -87,6 +92,10 @@
 struct TransparentRangeEqualTo {
   using is_transparent = void;
 
+  template <typename TOtherHash>
+  using is_compatible =
+      std::is_base_of<detail::TransparentRangeEqualTo<T>, TOtherHash>;
+
   template <typename U1, typename U2>
   bool operator()(U1 const& lhs, U2 const& rhs) const {
     return Range<T const*>{lhs} == Range<T const*>{rhs};
@@ -105,6 +114,10 @@
   using is_transparent = void;
   using folly_is_avalanching = std::true_type;
 
+  template <typename TOtherHash>
+  using is_compatible =
+      std::is_base_of<detail::TransparentRangeHash<T>, TOtherHash>;
+
   template <typename U>
   std::size_t operator()(U const& stringish) const {
     return hasher<Range<T const*>>{}(Range<T const*>{stringish});
@@ -116,11 +129,15 @@
   using is_transparent = void;
   using folly_is_avalanching = std::true_type;
 
+  template <typename TOtherHash>
+  using is_compatible =
+      std::is_base_of<detail::TransparentRangeHash<char>, TOtherHash>;
+
   template <typename U>
   std::size_t operator()(U const& stringish) const {
     auto sp = StringPiece{stringish};
-    return static_cast<std::size_t>(
-        folly::hash::rapidhashNano(sp.data(), sp.size()));
+    return static_cast<std::size_t>(folly::hash::rapidhashNano_with_seed(
+        sp.data(), sp.size(), hash::detail::RandomSeed::seed()));
   }
 };
 
@@ -145,6 +162,15 @@
     EligibleForHeterogeneousFind<TableKey, Hasher, KeyEqual, ArgKey>,
     std::is_constructible<TableKey, ArgKey>>;
 
+template <typename T, typename OtherT, typename Enable = void>
+struct HasCompatibleTest : std::false_type {};
+
+template <typename T, typename OtherT>
+struct HasCompatibleTest<
+    T,
+    OtherT,
+    void_t<typename T::template is_compatible<OtherT>>> : std::true_type {};
+
 } // namespace detail
 
 template <typename T>
@@ -162,5 +188,37 @@
     : detail::TransparentRangeHash<
           typename detail::ValueTypeForTransparentConversionToRange<T>::type> {
 };
+
+template <typename Hash1, typename Hash2, typename Enable = void>
+struct HeterogeneousPreHashCompatible : std::is_same<Hash1, Hash2> {};
+
+template <typename Hash1, typename Hash2>
+struct HeterogeneousPreHashCompatible<
+    Hash1,
+    Hash2,
+    std::enable_if_t<
+        detail::HasCompatibleTest<Hash1, Hash2>::value &&
+        detail::HasCompatibleTest<Hash2, Hash1>::value>>
+    : Disjunction<
+          typename Hash1::template is_compatible<Hash2>,
+          typename Hash2::template is_compatible<Hash1>> {};
+
+template <typename Hash1, typename Hash2>
+struct HeterogeneousPreHashCompatible<
+    Hash1,
+    Hash2,
+    std::enable_if_t<
+        detail::HasCompatibleTest<Hash1, Hash2>::value &&
+        !detail::HasCompatibleTest<Hash2, Hash1>::value>>
+    : Hash1::template is_compatible<Hash2> {};
+
+template <typename Hash1, typename Hash2>
+struct HeterogeneousPreHashCompatible<
+    Hash1,
+    Hash2,
+    std::enable_if_t<
+        !detail::HasCompatibleTest<Hash1, Hash2>::value &&
+        detail::HasCompatibleTest<Hash2, Hash1>::value>>
+    : Hash2::template is_compatible<Hash1> {};
 
 } // namespace folly
diff --git a/folly/folly/container/IntrusiveHeap.h b/folly/folly/container/IntrusiveHeap.h
--- a/folly/folly/container/IntrusiveHeap.h
+++ b/folly/folly/container/IntrusiveHeap.h
@@ -31,6 +31,7 @@
 #include <boost/noncopyable.hpp>
 #include <glog/logging.h>
 #include <folly/Portability.h>
+#include <folly/lang/Builtin.h>
 
 namespace folly {
 
@@ -74,7 +75,7 @@
   static T* asT(IntrusiveHeapNode<Tag>* n) { return static_cast<T*>(n); }
 };
 
-template <class T, class Tag, IntrusiveHeapNode<Tag> T::*PtrToMember>
+template <class T, class Tag, IntrusiveHeapNode<Tag> T::* PtrToMember>
 struct MemberNodeTraits {
   static IntrusiveHeapNode<Tag>* asNode(T* x) { return &(x->*PtrToMember); }
   static T* asT(IntrusiveHeapNode<Tag>* n) {
@@ -229,7 +230,7 @@
     }
     do {
       Node* grandparent = parent;
-      if (compare(a, b)) {
+      if (FOLLY_BUILTIN_UNPREDICTABLE(compare(a, b))) {
         parent = b;
       } else {
         parent = a;
diff --git a/folly/folly/container/IntrusiveList.h b/folly/folly/container/IntrusiveList.h
--- a/folly/folly/container/IntrusiveList.h
+++ b/folly/folly/container/IntrusiveList.h
@@ -61,7 +61,7 @@
  * The elements stored in the list must contain an IntrusiveListHook member
  * variable.
  */
-template <typename T, IntrusiveListHook T::*PtrToMember>
+template <typename T, IntrusiveListHook T::* PtrToMember>
 using IntrusiveList = boost::intrusive::list<
     T,
     boost::intrusive::member_hook<T, IntrusiveListHook, PtrToMember>,
@@ -80,7 +80,7 @@
  * that the hook is initialised to an unlinked state on construction and reset
  * an unlinked state upon removing it from a list.
  */
-template <typename T, SafeIntrusiveListHook T::*PtrToMember>
+template <typename T, SafeIntrusiveListHook T::* PtrToMember>
 using SafeIntrusiveList = boost::intrusive::list<
     T,
     boost::intrusive::member_hook<T, SafeIntrusiveListHook, PtrToMember>,
@@ -122,7 +122,7 @@
  * The elements stored in the list must contain an SafeIntrusiveListHook member
  * variable.
  */
-template <typename T, SafeIntrusiveListHook T::*PtrToMember>
+template <typename T, SafeIntrusiveListHook T::* PtrToMember>
 using CountedIntrusiveList = boost::intrusive::list<
     T,
     boost::intrusive::member_hook<T, SafeIntrusiveListHook, PtrToMember>,
diff --git a/folly/folly/container/Iterator.h b/folly/folly/container/Iterator.h
--- a/folly/folly/container/Iterator.h
+++ b/folly/folly/container/Iterator.h
@@ -472,8 +472,7 @@
  * aliases cannot be used for CRTP.
  */
 template <
-    template <typename>
-    class EmplaceImplT,
+    template <typename> class EmplaceImplT,
     typename Container,
     bool implicit_unpack>
 class emplace_iterator_impl
diff --git a/folly/folly/container/MapUtil.h b/folly/folly/container/MapUtil.h
--- a/folly/folly/container/MapUtil.h
+++ b/folly/folly/container/MapUtil.h
@@ -125,6 +125,20 @@
 }
 
 /**
+ * Given a map and a key, return a OptionalValue if the key exists and None if
+ * the key does not exist in the map.
+ */
+template <class OptionalValue, class Map, typename Key = typename Map::key_type>
+OptionalValue get_optional(const Map& map, const Key& key) {
+  auto pos = map.find(key);
+  if (pos != map.end()) {
+    return OptionalValue(pos->second);
+  } else {
+    return {};
+  }
+}
+
+/**
  * Given a map and a key, return a reference to the value corresponding to the
  * key in the map, or the given default reference if the key doesn't exist in
  * the map.
@@ -327,9 +341,8 @@
     Map* FOLLY_NULLABLE map,
     const Key1& key1,
     const Key2& key2,
-    const Keys&... keys) ->
-    typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::
-        type* FOLLY_NULLABLE {
+    const Keys&... keys) -> typename detail::
+    NestedMapType<Map, 2 + sizeof...(Keys)>::type* FOLLY_NULLABLE {
   return map ? get_ptr(*map, key1, key2, keys...) : nullptr;
 }
 
diff --git a/folly/folly/container/Merge.h b/folly/folly/container/Merge.h
--- a/folly/folly/container/Merge.h
+++ b/folly/folly/container/Merge.h
@@ -15,7 +15,7 @@
  */
 
 /*
- * folly::merge() is an implementation of std::merge with one additonal
+ * folly::merge() is an implementation of std::merge with one additional
  * guarantee: if the input ranges overlap, the order that values *from the two
  * different ranges* appear in the output is well defined (std::merge only
  * guarantees relative ordering is maintained within a single input range).
diff --git a/folly/folly/container/RegexMatchCache.cpp b/folly/folly/container/RegexMatchCache.cpp
--- a/folly/folly/container/RegexMatchCache.cpp
+++ b/folly/folly/container/RegexMatchCache.cpp
@@ -27,7 +27,6 @@
 #include <folly/MapUtil.h>
 #include <folly/String.h>
 #include <folly/container/Reserve.h>
-#include <folly/ssl/OpenSSLHash.h>
 #include <folly/synchronization/AtomicUtil.h>
 
 namespace folly {
@@ -36,13 +35,6 @@
   return fmt::format("\"{}\"", cEscape<std::string>(s));
 }
 
-RegexMatchCacheKey::data_type RegexMatchCacheKey::init(
-    std::string_view const regex) noexcept {
-  data_type data;
-  folly::ssl::OpenSSLHash::sha256(range(data), StringPiece(regex));
-  return data;
-}
-
 class RegexMatchCache::RegexObject {
  private:
   boost::regex object;
@@ -156,7 +148,7 @@
   auto const q = [](std::string_view const s) { return quote(s); };
   auto const h = report;
 
-  auto const r = [&](regex_key const r) { return keys.lookup(r); };
+  auto const k = [&](regex_key const r) { return keys.lookup(r); };
 
   if (cacheRegexToMatch_.empty() || cacheMatchToRegex_.empty()) {
     if (!stringQueueForward_.empty()) {
@@ -171,7 +163,7 @@
   //  check that caches are bidi-consistent
   //  check that missing cache entries are found in string-queues
   for (auto const& [regex, rtmentry] : cacheRegexToMatch_) {
-    auto const regexs = r(regex);
+    auto const regexs = k(regex);
     auto const regexi = regexVector_.index_of_value(&regex);
     for (auto const& [match, mtrentry] : cacheMatchToRegex_) {
       auto const rtmcontains = rtmentry.matches.contains(match);
@@ -243,11 +235,11 @@
       if (!sqrptr) {
         h(fmt::format( //
             "string-queue-reverse none regex[{}]",
-            q(r(*regex))));
+            q(k(*regex))));
       } else if (!sqrptr->strings.contains(string)) {
         h(fmt::format( //
             "string-queue-reverse[{}] none string[{}]",
-            q(r(*regex)),
+            q(k(*regex)),
             q(*string)));
       }
       auto const mtrhas = mtrptr && mtrptr->regexes.get_value(regexi);
@@ -257,7 +249,7 @@
         h(fmt::format( //
             "string-queue-forward[{}] has regex[{}]",
             q(*string),
-            q(r(*regex))));
+            q(k(*regex))));
       }
     }
   }
@@ -274,7 +266,7 @@
         h(fmt::format( //
             "string-queue-forward[{}] none regex[{}]",
             q(*string),
-            q(r(*regex))));
+            q(k(*regex))));
       }
       auto const mtrptr = get_ptr(cacheMatchToRegex_, string);
       auto const mtrhas = mtrptr && mtrptr->regexes.get_value(regexi);
@@ -282,7 +274,7 @@
       if (mtrhas || rtmhas) {
         h(fmt::format( //
             "string-queue-reverse[{}] has string[{}]",
-            q(r(*regex)),
+            q(k(*regex)),
             q(*string)));
       }
     }
diff --git a/folly/folly/container/RegexMatchCache.h b/folly/folly/container/RegexMatchCache.h
--- a/folly/folly/container/RegexMatchCache.h
+++ b/folly/folly/container/RegexMatchCache.h
@@ -32,7 +32,7 @@
 #include <folly/container/F14Map.h>
 #include <folly/container/F14Set.h>
 #include <folly/container/Reserve.h>
-#include <folly/container/span.h>
+#include <folly/hash/UniqueHashKey.h>
 #include <folly/lang/Bits.h>
 
 namespace folly {
@@ -373,6 +373,8 @@
   folly::F14FastMap<size_t, Value> reverse_;
 };
 
+using RegexMatchCacheKeyBase = unique_hash_key_strong_sha256<32>;
+
 /// RegexMatchCacheKey
 ///
 /// A key derived from a string. Used with RegexMatchCache.
@@ -380,47 +382,10 @@
 /// Intended for use only with RegexMatchCache.
 ///
 /// Incomplete as a generic facility.
-class RegexMatchCacheKey {
- private:
-  using self = RegexMatchCacheKey;
-
-  static inline constexpr size_t data_size = 32;
-  static inline constexpr size_t data_align = alignof(size_t);
-
-  using data_type = std::array<unsigned char, data_size>;
-
-  alignas(data_align) data_type const data_;
-
-  static data_type init(std::string_view regex) noexcept;
-
-  template <typename T, size_t E, typename V = std::remove_cv_t<T>>
-  static constexpr bool is_span_compatible_v = //
-      !std::is_volatile_v<T> && //
-      std::is_integral_v<V> && //
-      std::is_unsigned_v<V> && //
-      !std::is_same_v<bool, V> && //
-      !std::is_same_v<char, V> && //
-      alignof(V) <= data_align && //
-      (E == data_size / sizeof(T) || E == dynamic_extent);
-
+class RegexMatchCacheKey : public RegexMatchCacheKeyBase {
  public:
   explicit RegexMatchCacheKey(std::string_view regex) noexcept
-      : data_{init(regex)} {}
-
-  template <
-      typename T,
-      std::size_t E,
-      std::enable_if_t<is_span_compatible_v<T, E>, int> = 0>
-  explicit operator span<T const, E>() const noexcept {
-    return {reinterpret_cast<T const*>(data_.data()), E};
-  }
-
-  friend auto operator==(self const& a, self const& b) noexcept {
-    return a.data_ == b.data_;
-  }
-  friend auto operator!=(self const& a, self const& b) noexcept {
-    return a.data_ != b.data_;
-  }
+      : RegexMatchCacheKeyBase{std::tuple(regex)} {}
 };
 
 } // namespace folly
@@ -428,13 +393,8 @@
 namespace std {
 
 template <>
-struct hash<::folly::RegexMatchCacheKey> {
-  using folly_is_avalanching = std::true_type;
-
-  size_t operator()(::folly::RegexMatchCacheKey const& key) const noexcept {
-    return ::folly::span<size_t const>{key}[0];
-  }
-};
+struct hash<::folly::RegexMatchCacheKey>
+    : hash<::folly::RegexMatchCacheKeyBase> {};
 
 } // namespace std
 
diff --git a/folly/folly/container/StdBitset.h b/folly/folly/container/StdBitset.h
--- a/folly/folly/container/StdBitset.h
+++ b/folly/folly/container/StdBitset.h
@@ -19,24 +19,92 @@
 #include <folly/CPortability.h>
 #include <folly/Portability.h>
 
+#include <bit>
 #include <bitset>
 #include <cassert>
 
 namespace folly {
 
+#ifdef _LIBCPP_VERSION
+
+constexpr size_t kWordSize = 64;
+
+namespace libcpp_detail {
+struct std_bit_reference_layout {
+  size_t* address;
+  size_t mask;
+};
+
+// Return the content of the 64-bit word, given an index.
+template <size_t N>
+size_t* get_underlying_data_region(const std::bitset<N>& bitset) {
+  // You are only getting the __bit_reference as the return if you access
+  // with operator[], which is not const.
+  auto& bitset_ref = const_cast<std::bitset<N>&>(bitset);
+  std::__bit_reference bitref = bitset_ref[0];
+  auto* br =
+      reinterpret_cast<std_bit_reference_layout*>(std::addressof(bitref));
+  return br->address;
+}
+
+template <size_t N>
+size_t std_bitset_find_next(const std::bitset<N>& bitset, size_t start) {
+  constexpr size_t max_words = N / kWordSize + 1;
+  if constexpr (N == 0) {
+    return 1;
+  }
+
+  // Check starting in the middle of a word
+  size_t start_in_word = start % kWordSize;
+  size_t word_idx = start / kWordSize;
+  size_t* data = get_underlying_data_region(bitset);
+
+  if (start_in_word) {
+    // Invariant: start != 0, 1 <= start_in_word <= 63
+    // test the remaining bits in the same word as previous
+    size_t mask = (~size_t(0)) << start_in_word;
+    size_t partial_word = data[word_idx] & mask;
+
+    size_t idx_in_word = std::countr_zero(partial_word);
+    if (idx_in_word != kWordSize) {
+      // We found the next
+      return start - start_in_word + idx_in_word;
+    }
+    // Special treatment of the partial first word was done. Skip it in the
+    // next loop for whole words.
+    word_idx += 1;
+  }
+
+  while (word_idx < max_words) {
+    size_t word = data[word_idx];
+    size_t idx_in_word = std::countr_zero(word);
+    if (idx_in_word != kWordSize) {
+      // we found the first
+      return word_idx * kWordSize + idx_in_word;
+    }
+    word_idx += 1;
+  }
+
+  return N;
+}
+} // namespace libcpp_detail
+
+#endif // _LIBCPP_VERSION
+
 struct std_bitset_find_first_fn {
   /// Return the index of the first set bit in a bitset, or bitset.size() if
   /// none.
   template <size_t N>
   FOLLY_ALWAYS_INLINE size_t
   operator()(const std::bitset<N>& bitset) const noexcept {
-    // equivalent to #if defined(__GLIBCXX__)
-    if constexpr (kIsGlibcxx) {
-      // GNU provides non-standard (its a hold over from the original SGI
-      // implementation) _Find_first(), which efficiently returns the index of
-      // the first set bit.
-      return bitset._Find_first();
-    }
+#if defined(__GLIBCXX__)
+    // GNU provides non-standard (its a hold over from the original SGI
+    // implementation) _Find_first(), which efficiently returns the index of
+    // the first set bit.
+    return bitset._Find_first();
+#elif defined(_LIBCPP_VERSION)
+    return folly::libcpp_detail::std_bitset_find_next(bitset, 0);
+#endif
 
     for (size_t i = 0; i < bitset.size(); ++i) {
       if (bitset[i]) {
@@ -58,13 +126,14 @@
   operator()(const std::bitset<N>& bitset, size_t prev) const noexcept {
     assert(prev < bitset.size());
 
-    // equivalent to #if defined(__GLIBCXX__)
-    if constexpr (kIsGlibcxx) {
-      // GNU provides non-standard (its a hold over from the original SGI
-      // implementation) _Find_next(), which given an index, efficiently returns
-      // the index of the first set bit after the index.
-      return bitset._Find_next(prev);
-    }
+#if defined(__GLIBCXX__)
+    // GNU provides non-standard (its a hold over from the original SGI
+    // implementation) _Find_next(), which given an index, efficiently returns
+    // the index of the first set bit after the index.
+    return bitset._Find_next(prev);
+#elif defined(_LIBCPP_VERSION)
+    return folly::libcpp_detail::std_bitset_find_next(bitset, prev + 1);
+#endif
 
     for (size_t i = prev + 1; i < bitset.size(); ++i) {
       if (bitset[i]) {
diff --git a/folly/folly/container/detail/F14Policy.h b/folly/folly/container/detail/F14Policy.h
--- a/folly/folly/container/detail/F14Policy.h
+++ b/folly/folly/container/detail/F14Policy.h
@@ -1487,8 +1487,13 @@
 };
 
 template <
-    template <typename, typename, typename, typename, typename, typename...>
-    class Policy,
+    template <
+        typename,
+        typename,
+        typename,
+        typename,
+        typename,
+        typename...> class Policy,
     typename Key,
     typename Mapped,
     typename Hasher,
@@ -1504,8 +1509,13 @@
     Args...>;
 
 template <
-    template <typename, typename, typename, typename, typename, typename...>
-    class Policy,
+    template <
+        typename,
+        typename,
+        typename,
+        typename,
+        typename,
+        typename...> class Policy,
     typename Key,
     typename Hasher,
     typename KeyEqual,
diff --git a/folly/folly/container/detail/F14Table.h b/folly/folly/container/detail/F14Table.h
--- a/folly/folly/container/detail/F14Table.h
+++ b/folly/folly/container/detail/F14Table.h
@@ -161,18 +161,11 @@
   V value;
 };
 
-#else
+#elif defined(__GLIBCXX__)
 
-template <typename H>
-struct StdIsFastHash : std::true_type {};
-template <>
-struct StdIsFastHash<std::hash<long double>> : std::false_type {};
-template <typename... Args>
-struct StdIsFastHash<std::hash<std::basic_string<Args...>>> : std::false_type {
-};
-template <typename... Args>
-struct StdIsFastHash<std::hash<std::basic_string_view<Args...>>>
-    : std::false_type {};
+template <typename K, typename H>
+constexpr bool kStdNodeContainsHash =
+    !std::__is_fast_hash<H>::value || !is_nothrow_invocable_v<H, K const&>;
 
 // mimic internal node of unordered containers in STL to estimate the size
 template <typename K, typename V, typename H, typename Enable = void>
@@ -181,17 +174,20 @@
   V value;
 };
 template <typename K, typename V, typename H>
-struct StdNodeReplica<
-    K,
-    V,
-    H,
-    std::enable_if_t<
-        !StdIsFastHash<H>::value || !is_nothrow_invocable_v<H, K>>> {
+struct StdNodeReplica<K, V, H, std::enable_if_t<kStdNodeContainsHash<K, H>>> {
   void* next;
   V value;
   std::size_t hash;
 };
 
+#else
+
+template <typename K, typename V, typename H>
+struct StdNodeReplica {
+  void* next;
+  V value;
+};
+
 #endif
 
 template <class Container, class Predicate>
@@ -1742,8 +1738,8 @@
   template <typename HKKey, typename HKHasher, typename HKEqual>
   HashPair computeHash(
       F14HashedKey<HKKey, HKHasher, HKEqual> const& hashedKey) const {
-    static_assert(std::is_same_v<HKHasher, Hasher>);
-    static_assert(std::is_same_v<HKEqual, KeyEqual>);
+    static_assert(HeterogeneousPreHashCompatible<Hasher, HKHasher>::value);
+    static_assert(HeterogeneousPreHashCompatible<HKEqual, KeyEqual>::value);
     return static_cast<HashPair>(hashedKey.getHashToken());
   }
 
@@ -1772,6 +1768,20 @@
 
   template <typename K>
   FOLLY_ALWAYS_INLINE ItemIter find(K const& key) const {
+    const auto sz = size();
+    if (sz <= 1) {
+      if (sz == 0) {
+        return ItemIter{};
+      }
+      // There is no easy way to obtain a begin iterator with the current policy
+      // design.
+      // As a result, F14Vector* containers (kEnableItemIteration == false) do
+      // not benefit from this optimization yet.
+      if constexpr (kEnableItemIteration) {
+        ItemIter beg = begin();
+        return this->keyMatchesItem(key, beg.citem()) ? beg : ItemIter{};
+      }
+    }
     auto hp = computeHash(key);
     return findImpl(hp, key, Prefetch::ENABLED);
   }
@@ -1856,6 +1866,12 @@
     }
   }
 
+  [[FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE static void eraseBlankCold(
+      F14Table* self, ItemIter iter, HashPair hp) {
+    self->eraseBlank(iter, hp);
+    rethrow_current_exception();
+  }
+
   void adjustSizeAndBeginBeforeErase(ItemIter iter) {
     sizeAndChunkShiftAndPackedBegin_.decrementSize();
     if constexpr (kEnableItemIteration) {
@@ -1872,13 +1888,15 @@
 
   template <typename... Args>
   void insertAtBlank(ItemIter pos, HashPair hp, Args&&... args) {
-    try {
-      auto dst = pos.itemAddr();
-      this->constructValueAtItem(*this, dst, std::forward<Args>(args)...);
-    } catch (...) {
-      eraseBlank(pos, hp);
-      throw;
-    }
+    auto dst = pos.itemAddr();
+    catch_exception(
+        [&] {
+          this->constructValueAtItem(*this, dst, std::forward<Args>(args)...);
+        },
+        &eraseBlankCold,
+        this,
+        pos,
+        hp);
     adjustSizeAndBeginAfterInsert(pos);
   }
 
@@ -2102,6 +2120,13 @@
     success = true;
   }
 
+  [[FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE static void buildFromF14TableCatchCold(
+      F14Table* self) {
+    self->reset();
+    F14LinkCheck<getF14IntrinsicsMode()>::check();
+    rethrow_current_exception();
+  }
+
   template <typename T>
   FOLLY_NOINLINE void buildFromF14Table(T&& src) {
     FOLLY_SAFE_DCHECK(bucket_count() == 0, "");
@@ -2122,17 +2147,16 @@
     }
     rehashImpl(0, 1, 0, ccas.first, ccas.second);
 
-    try {
-      if (chunkShift() == src.chunkShift()) {
-        directBuildFrom(std::forward<T>(src));
-      } else {
-        rehashBuildFrom(std::forward<T>(src));
-      }
-    } catch (...) {
-      reset();
-      F14LinkCheck<getF14IntrinsicsMode()>::check();
-      throw;
-    }
+    catch_exception(
+        [&] {
+          if (chunkShift() == src.chunkShift()) {
+            directBuildFrom(std::forward<T>(src));
+          } else {
+            rehashBuildFrom(std::forward<T>(src));
+          }
+        },
+        &F14Table::buildFromF14TableCatchCold,
+        this);
   }
 
   void maybeRehash(std::size_t desiredCapacity, bool attemptExact) {
@@ -2227,7 +2251,9 @@
     std::size_t newChunkCount;
     std::size_t newCapacityScale;
     std::tie(newChunkCount, newCapacityScale) = computeChunkCountAndScale(
-        desiredCapacity, /*attemptExact=*/true, kContinuousCapacity);
+        desiredCapacity,
+        /*continuousSingleChunkCapacity=*/true,
+        kContinuousCapacity);
     auto newCapacity = computeCapacity(newChunkCount, newCapacityScale);
     auto newAllocSize = chunkAllocSize(newChunkCount, newCapacityScale);
 
@@ -2625,11 +2651,9 @@
       // force recycling of heap memory
       auto bc = bucket_count();
       reset();
-      try {
-        reserveImpl(bc);
-      } catch (std::bad_alloc const&) {
-        // ASAN mode only, keep going
-      }
+      catch_exception<std::bad_alloc const&>(
+          [this, bc]() { reserveImpl(bc); },
+          &folly::detail::thunk::noop<std::bad_alloc const&>);
     } else {
       clearImpl<false>();
     }
@@ -2823,8 +2847,9 @@
 namespace test {
 inline void disableInsertOrderRandomization() {
   if constexpr (kIsLibrarySanitizeAddress || kIsDebug) {
-    detail::tlsPendingSafeInserts(static_cast<std::ptrdiff_t>(
-        (std::numeric_limits<std::size_t>::max)() / 2));
+    detail::tlsPendingSafeInserts(
+        static_cast<std::ptrdiff_t>(
+            (std::numeric_limits<std::size_t>::max)() / 2));
   }
 }
 } // namespace test
diff --git a/folly/folly/container/detail/Util.h b/folly/folly/container/detail/Util.h
--- a/folly/folly/container/detail/Util.h
+++ b/folly/folly/container/detail/Util.h
@@ -146,9 +146,9 @@
             UsableAsKey<remove_cvref_t<Arg1>>::value,
         int> = 0>
 auto callWithExtractedKey(
-    Alloc&,
+    Alloc& /*unused*/,
     Func&& f,
-    std::piecewise_construct_t,
+    std::piecewise_construct_t /*unused*/,
     std::tuple<Arg1>&& first_args,
     std::tuple<Args2...>&& second_args) {
   // we found a usable key in the args :)
@@ -171,7 +171,7 @@
 auto callWithExtractedKey(
     Alloc& a,
     Func&& f,
-    std::piecewise_construct_t,
+    std::piecewise_construct_t /*unused*/,
     std::tuple<Args1...>&& first_args,
     std::tuple<Args2...>&& second_args) {
   // we will need to materialize a temporary key :(
@@ -266,7 +266,7 @@
         std::is_same<remove_cvref_t<Arg>, KeyType>::value ||
             UsableAsKey<remove_cvref_t<Arg>>::value,
         int> = 0>
-auto callWithConstructedKey(Alloc&, Func&& f, Arg&& arg) {
+auto callWithConstructedKey(Alloc& /*unused*/, Func&& f, Arg&& arg) {
   // we found a usable key in the args :)
   auto const& key = arg;
   return f(key, std::forward<Arg>(arg));
diff --git a/folly/folly/container/heap_vector_types.h b/folly/folly/container/heap_vector_types.h
--- a/folly/folly/container/heap_vector_types.h
+++ b/folly/folly/container/heap_vector_types.h
@@ -248,10 +248,12 @@
   end++;
   std::function<bool(size_type, size_type)> calculateDistance =
       [&](size_type offset, size_type lb) {
-        if (offset > size)
+        if (offset > size) {
           return false;
-        for (; offset <= size; offset <<= 1)
+        }
+        for (; offset <= size; offset <<= 1) {
           ;
+        }
         offset >>= 1;
         for (; offset > lb; offset >>= 1) {
           if (offset == start) {
@@ -835,8 +837,9 @@
       assert(cont_ == rawIterator.cont_);
       size_type offset0 = ptr_ - cont_->begin();
       size_type offset1 = rawIterator.ptr_ - cont_->begin();
-      if (offset1 == offset0)
+      if (offset1 == offset0) {
         return 0;
+      }
       return heap_vector_detail::distance(*cont_, offset1, offset0);
     }
 
@@ -844,8 +847,9 @@
       assert(cont_ == rawIterator.cont_);
       size_type offset0 = ptr_ - cont_->begin();
       size_type offset1 = rawIterator.ptr_ - cont_->begin();
-      if (offset1 == offset0)
+      if (offset1 == offset0) {
         return 0;
+      }
       return heap_vector_detail::distance(*cont_, offset1, offset0);
     }
 
@@ -1692,8 +1696,9 @@
         return offset - 1;
       }
       offset = 2 * offset + lt;
-      if (offset > size)
+      if (offset > size) {
         return size;
+      }
       cur_k = self.m_.getKey(cont[offset - 1]);
     }
     return size;
diff --git a/folly/folly/container/irange.h b/folly/folly/container/irange.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/container/irange.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <concepts>
+#include <ranges>
+#include <type_traits>
+
+namespace folly {
+
+namespace detail {
+template <typename T>
+concept IRangeNonBoolIntegral = std::integral<T> && !std::same_as<T, bool>;
+}
+
+/// Creates an integer range for the half-open interval [begin, end)
+/// If end<=begin, then the range is empty.
+/// The range has the type of the `end` integer; `begin` integer is
+/// cast to this type.
+///
+/// Instead of
+///   for (int i = 3; i < 10; ++i) {
+/// use
+///   for (const auto i : irange(3, 10)) {
+template <
+    detail::IRangeNonBoolIntegral Integer1,
+    detail::IRangeNonBoolIntegral Integer2>
+constexpr std::ranges::iota_view<
+    std::common_type_t<Integer1, Integer2>,
+    std::common_type_t<Integer1, Integer2>>
+irange(Integer1 begin, Integer2 end) {
+  // If end<=begin then the range is empty; we can achieve this effect by
+  // choosing the larger of {begin, end} as the loop terminator
+  using otype = std::common_type_t<Integer1, Integer2>;
+  return {
+      static_cast<otype>(begin),
+      std::max(static_cast<otype>(begin), static_cast<otype>(end))};
+}
+
+/// Creates an integer range for the half-open interval [0, end)
+///
+/// Instead of
+///   for (int i = 0; i < 10; ++i) {
+/// use
+///   for (const auto i : irange(10)) {
+///
+/// NOTE! This behaviour differs from `iota_view(N)` which iterates
+/// starts at `N` and loops over the full range of valus of `N`'s data
+/// type forever.
+template <detail::IRangeNonBoolIntegral Integer>
+constexpr std::ranges::iota_view<Integer, Integer> irange(Integer end) {
+  return {Integer(), end};
+}
+
+} // namespace folly
diff --git a/folly/folly/container/small_vector.h b/folly/folly/container/small_vector.h
--- a/folly/folly/container/small_vector.h
+++ b/folly/folly/container/small_vector.h
@@ -140,6 +140,17 @@
 namespace small_vector_detail {
 
 /*
+ * Just because a type is trivially copyable doesn't mean we should copy
+ * it. A copy constructor that is deleted is still considered trivial.
+ *
+ * If a type is not move constructible, it shouldn't be relocated. We
+ * should avoid resizing in general.
+ */
+template <typename T>
+inline constexpr bool should_trivially_copy =
+    std::is_trivially_copyable_v<T> && std::is_move_constructible_v<T>;
+
+/*
  * Move objects in memory to the right into some uninitialized memory, where
  * the region overlaps. Then call create() for each hole in reverse order.
  *
@@ -150,7 +161,7 @@
  * extra copies and moves for non-trivial types.
  */
 template <class T, class Create>
-typename std::enable_if<!std::is_trivially_copyable_v<T>>::type
+typename std::enable_if<!should_trivially_copy<T>>::type
 moveObjectsRightAndCreate(
     T* const first,
     T* const lastConstructed,
@@ -204,7 +215,7 @@
 // memory may be uninitialized, and std::move_backward() won't work when it
 // can't memmove().
 template <class T, class Create>
-typename std::enable_if<std::is_trivially_copyable_v<T>>::type
+typename std::enable_if<should_trivially_copy<T>>::type
 moveObjectsRightAndCreate(
     T* const first,
     T* const lastConstructed,
@@ -256,7 +267,7 @@
 
 template <class SizeType, bool ShouldUseHeap, bool AlwaysUseHeap>
 struct IntegralSizePolicyBase {
-  typedef SizeType InternalSizeType;
+  using InternalSizeType = SizeType;
 
   IntegralSizePolicyBase() : size_(0) {}
 
@@ -342,7 +353,8 @@
    * ranges don't overlap.
    */
   template <class T>
-  typename std::enable_if<!std::is_trivially_copyable_v<T>>::type
+  typename std::enable_if<
+      !detail::small_vector_detail::should_trivially_copy<T>>::type
   moveToUninitialized(T* first, T* last, T* out) {
     std::size_t idx = 0;
     {
@@ -363,7 +375,8 @@
 
   // Specialization for trivially copyable types.
   template <class T>
-  typename std::enable_if<std::is_trivially_copyable_v<T>>::type
+  typename std::enable_if<
+      detail::small_vector_detail::should_trivially_copy<T>>::type
   moveToUninitialized(T* first, T* last, T* out) {
     std::memmove(
         static_cast<void*>(out),
@@ -447,21 +460,19 @@
   /*
    * Make the real policy base classes.
    */
-  typedef IntegralSizePolicy<
+  using ActualSizePolicy = IntegralSizePolicy<
       typename Policy::size_type,
       !Policy::in_situ_only::value,
-      RequestedMaxInline == 0>
-      ActualSizePolicy;
+      RequestedMaxInline == 0>;
 
   /*
    * Now inherit from them all.  This is done in such a convoluted
    * way to make sure we get the empty base optimization on all these
    * types to keep sizeof(small_vector<>) minimal.
    */
-  typedef boost::totally_ordered1<
+  using type = boost::totally_ordered1<
       small_vector<Value, RequestedMaxInline, InPolicy>,
-      ActualSizePolicy>
-      type;
+      ActualSizePolicy>;
 };
 
 namespace small_vector_detail {
@@ -488,9 +499,9 @@
 class small_vector
     : public detail::small_vector_base<Value, RequestedMaxInline, Policy>::
           type {
-  typedef typename detail::
-      small_vector_base<Value, RequestedMaxInline, Policy>::type BaseType;
-  typedef typename BaseType::InternalSizeType InternalSizeType;
+  using BaseType = typename detail::
+      small_vector_base<Value, RequestedMaxInline, Policy>::type;
+  using InternalSizeType = typename BaseType::InternalSizeType;
 
   /*
    * Figure out the max number of elements we should inline.  (If
@@ -505,19 +516,19 @@
           : constexpr_max(kSizeOfValuePtr / kSizeOfValue, RequestedMaxInline)};
 
  public:
-  typedef std::size_t size_type;
-  typedef Value value_type;
-  typedef std::allocator<Value> allocator_type;
-  typedef value_type& reference;
-  typedef value_type const& const_reference;
-  typedef value_type* iterator;
-  typedef value_type* pointer;
-  typedef value_type const* const_iterator;
-  typedef value_type const* const_pointer;
-  typedef std::ptrdiff_t difference_type;
+  using size_type = std::size_t;
+  using value_type = Value;
+  using allocator_type = std::allocator<Value>;
+  using reference = value_type&;
+  using const_reference = value_type const&;
+  using iterator = value_type*;
+  using pointer = value_type*;
+  using const_iterator = value_type const*;
+  using const_pointer = value_type const*;
+  using difference_type = std::ptrdiff_t;
 
-  typedef std::reverse_iterator<iterator> reverse_iterator;
-  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+  using reverse_iterator = std::reverse_iterator<iterator>;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
   small_vector() = default;
   // Allocator is unused here. It is taken in for compatibility with std::vector
@@ -533,7 +544,7 @@
     }
 
     auto n = o.size();
-    makeSize(n);
+    makeSize</* IgnoreExistingData */ true>(n);
     {
       auto rollback = makeGuard([&] { freeHeap(); });
       std::uninitialized_copy(o.begin(), o.begin() + n, begin());
@@ -867,12 +878,13 @@
   }
 
   void shrink_to_fit() {
-    if (!this->isExtern()) {
+    if (!this->isExtern() || size() == capacity()) {
       return;
     }
 
-    small_vector tmp(begin(), end());
-    tmp.swap(*this);
+    small_vector old = std::exchange(*this, {});
+    reserve(old.size());
+    std::move(old.begin(), old.end(), std::back_inserter(*this));
   }
 
   template <class... Args>
@@ -1076,7 +1088,7 @@
   }
 
   void copyWholeInlineStorageTrivial(small_vector const& o) {
-    static_assert(std::is_trivially_copyable_v<Value>);
+    static_assert(detail::small_vector_detail::should_trivially_copy<Value>);
     FOLLY_PUSH_WARNING
     FOLLY_GCC_DISABLE_WARNING("-Warray-bounds")
     std::copy(o.u.buffer(), o.u.buffer() + MaxInline, u.buffer());
@@ -1090,9 +1102,15 @@
     FOLLY_PUSH_WARNING
     FOLLY_GCC_DISABLE_WARNING("-Wclass-memaccess")
     if constexpr (kMayCopyWholeInlineStorage) {
-      std::memcpy(u.buffer(), o.u.buffer(), MaxInline * kSizeOfValue);
+      std::memcpy(
+          reinterpret_cast<void*>(u.buffer()),
+          reinterpret_cast<const void*>(o.u.buffer()),
+          MaxInline * kSizeOfValue);
     } else {
-      std::memcpy(u.buffer(), o.u.buffer(), n * kSizeOfValue);
+      std::memcpy(
+          reinterpret_cast<void*>(u.buffer()),
+          reinterpret_cast<const void*>(o.u.buffer()),
+          n * kSizeOfValue);
     }
     FOLLY_POP_WARNING
     this->setSize(n);
@@ -1155,7 +1173,7 @@
   // constructor).
   template <class It>
   void constructImpl(It first, It last, std::false_type) {
-    typedef typename std::iterator_traits<It>::iterator_category categ;
+    using categ = typename std::iterator_traits<It>::iterator_category;
     if (std::is_same<categ, std::input_iterator_tag>::value) {
       // With iterators that only allow a single pass, we can't really
       // do anything sane here.
@@ -1185,7 +1203,7 @@
 
   template <typename InitFunc>
   void doConstruct(size_type n, InitFunc&& func) {
-    makeSize(n);
+    makeSize</* IgnoreExistingData */ true>(n);
     assert(size() == 0);
     this->incrementSize(n);
     {
@@ -1218,18 +1236,26 @@
     return static_cast<size_type>(std::min<size_t>(c, max_size()));
   }
 
+  template <bool IgnoreExistingData = false>
   void makeSize(size_type newSize) {
     if (newSize <= capacity()) {
       return;
     }
-    makeSizeInternal(newSize, false, [](void*) { assume_unreachable(); }, 0);
+    auto emplaceFunc = [](void*) { assume_unreachable(); };
+    makeSizeInternal<
+        /* Insert */ false,
+        IgnoreExistingData,
+        decltype(emplaceFunc)>(newSize, std::move(emplaceFunc), 0);
   }
 
   template <typename EmplaceFunc>
   void makeSize(size_type newSize, EmplaceFunc&& emplaceFunc, size_type pos) {
     assert(size() == capacity());
-    makeSizeInternal(
-        newSize, true, std::forward<EmplaceFunc>(emplaceFunc), pos);
+
+    makeSizeInternal<
+        /* Insert */ true,
+        /* IgnoreExistingData */ false,
+        EmplaceFunc>(newSize, std::forward<EmplaceFunc>(emplaceFunc), pos);
   }
 
   /*
@@ -1242,12 +1268,9 @@
    * NOTE: If reallocation is not needed, insert must be false,
    * because we only know how to emplace elements into new memory.
    */
-  template <typename EmplaceFunc>
+  template <bool Insert, bool IgnoreExistingData, typename EmplaceFunc>
   void makeSizeInternal(
-      size_type newSize,
-      bool insert,
-      EmplaceFunc&& emplaceFunc,
-      size_type pos) {
+      size_type newSize, EmplaceFunc&& emplaceFunc, size_type pos) {
     if (newSize > max_size()) {
       throw_exception<std::length_error>("max_size exceeded in small_vector");
     }
@@ -1300,11 +1323,12 @@
       auto rollback = makeGuard([&] { //
         sizedFree(newh, sizeBytes);
       });
-      if (insert) {
+      if constexpr (Insert) {
+        static_assert(!IgnoreExistingData);
         // move and insert the new element
         this->moveToUninitializedEmplace(
             begin(), end(), newp, pos, std::forward<EmplaceFunc>(emplaceFunc));
-      } else {
+      } else if constexpr (!IgnoreExistingData) {
         // move without inserting new element
         if (data()) {
           this->moveToUninitialized(begin(), end(), newp);
@@ -1372,13 +1396,13 @@
   FOLLY_SV_PACK_POP
 
   static constexpr size_t kMaxInlineNonZero = MaxInline ? MaxInline : 1u;
-  typedef aligned_storage_for_t<value_type[kMaxInlineNonZero]>
-      InlineStorageDataType;
+  using InlineStorageDataType =
+      aligned_storage_for_t<value_type[kMaxInlineNonZero]>;
 
-  typedef typename std::conditional<
+  using InlineStorageType = typename std::conditional<
       sizeof(value_type) * MaxInline != 0,
       InlineStorageDataType,
-      char>::type InlineStorageType;
+      char>::type;
 
   // If the storage is small enough, it is usually faster to copy it entirely,
   // instead of just size() values, to make the loop fixed-size and
@@ -1388,7 +1412,8 @@
       sizeof(InlineStorageType) <= hardware_constructive_interference_size / 2;
 
   static constexpr bool kShouldCopyWholeInlineStorageTrivial =
-      std::is_trivially_copyable_v<Value> && kMayCopyWholeInlineStorage;
+      detail::small_vector_detail::should_trivially_copy<Value> &&
+      kMayCopyWholeInlineStorage;
 
   static bool constexpr kHasInlineCapacity = !BaseType::kAlwaysUseHeap &&
       sizeof(HeapPtrWithCapacity) < sizeof(InlineStorageType);
@@ -1422,9 +1447,8 @@
   static bool constexpr kAlwaysHasCapacity =
       kHasInlineCapacity || kMustTrackHeapifiedCapacity;
 
-  typedef typename std::
-      conditional<kHasInlineCapacity, HeapPtrWithCapacity, HeapPtr>::type
-          PointerType;
+  using PointerType = typename std::
+      conditional<kHasInlineCapacity, HeapPtrWithCapacity, HeapPtr>::type;
 
   bool hasCapacity() const {
     return kAlwaysHasCapacity || !kHeapifyCapacityThreshold ||
diff --git a/folly/folly/container/sorted_vector_types.h b/folly/folly/container/sorted_vector_types.h
--- a/folly/folly/container/sorted_vector_types.h
+++ b/folly/folly/container/sorted_vector_types.h
@@ -106,7 +106,7 @@
 struct growth_policy_wrapper : private Policy {
   template <class Container, class Iterator>
   Iterator increase_capacity(Container& c, Iterator desired_insertion) {
-    typedef typename Container::difference_type diff_t;
+    using diff_t = typename Container::difference_type;
     diff_t d = desired_insertion - c.begin();
     Policy::increase_capacity(c);
     return c.begin() + d;
@@ -186,8 +186,9 @@
 
   ~DirectMutationGuard() noexcept(false) {
     if (isSortedUnique_) {
-      assert(detail::is_sorted_unique(
-          container_.begin(), container_.end(), comp_));
+      assert(
+          detail::is_sorted_unique(
+              container_.begin(), container_.end(), comp_));
       return;
     }
     as_sorted_unique(container_, comp_);
@@ -282,30 +283,30 @@
   struct EBO;
 
  public:
-  typedef T value_type;
-  typedef T key_type;
-  typedef Compare key_compare;
-  typedef Compare value_compare;
-  typedef Allocator allocator_type;
-  typedef Container container_type;
+  using value_type = T;
+  using key_type = T;
+  using key_compare = Compare;
+  using value_compare = Compare;
+  using allocator_type = Allocator;
+  using container_type = Container;
 
-  typedef typename Container::pointer pointer;
-  typedef typename Container::reference reference;
-  typedef typename Container::const_reference const_reference;
-  typedef typename Container::const_pointer const_pointer;
+  using pointer = typename Container::pointer;
+  using reference = typename Container::reference;
+  using const_reference = typename Container::const_reference;
+  using const_pointer = typename Container::const_pointer;
   /*
    * XXX: Our normal iterator ought to also be a constant iterator
    * (cf. Defect Report 103 for std::set), but this is a bit more of a
    * pain.
    */
-  typedef typename Container::iterator iterator;
-  typedef typename Container::const_iterator const_iterator;
-  typedef typename Container::difference_type difference_type;
-  typedef typename Container::size_type size_type;
-  typedef typename Container::reverse_iterator reverse_iterator;
-  typedef typename Container::const_reverse_iterator const_reverse_iterator;
-  typedef detail::DirectMutationGuard<Container, value_compare>
-      direct_mutation_guard;
+  using iterator = typename Container::iterator;
+  using const_iterator = typename Container::const_iterator;
+  using difference_type = typename Container::difference_type;
+  using size_type = typename Container::size_type;
+  using reverse_iterator = typename Container::reverse_iterator;
+  using const_reverse_iterator = typename Container::const_reverse_iterator;
+  using direct_mutation_guard =
+      detail::DirectMutationGuard<Container, value_compare>;
 
   sorted_vector_set() : m_(Compare(), Allocator()) {}
 
@@ -395,8 +396,9 @@
                                       const Compare&,
                                       Container&&>::value)
       : m_(comp, std::move(container)) {
-    assert(detail::is_sorted_unique(
-        m_.cont_.begin(), m_.cont_.end(), value_comp()));
+    assert(
+        detail::is_sorted_unique(
+            m_.cont_.begin(), m_.cont_.end(), value_comp()));
   }
 
   Allocator get_allocator() const { return m_.cont_.get_allocator(); }
@@ -444,8 +446,9 @@
     swap(m_.cont_, newContainer);
   }
   void swap_container(sorted_unique_t, Container& newContainer) {
-    assert(detail::is_sorted_unique(
-        newContainer.begin(), newContainer.end(), value_comp()));
+    assert(
+        detail::is_sorted_unique(
+            newContainer.begin(), newContainer.end(), value_comp()));
     using std::swap;
     swap(m_.cont_, newContainer);
   }
@@ -966,12 +969,12 @@
   struct EBO;
 
  public:
-  typedef Key key_type;
-  typedef Value mapped_type;
-  typedef typename Container::value_type value_type;
-  typedef Compare key_compare;
-  typedef Allocator allocator_type;
-  typedef Container container_type;
+  using key_type = Key;
+  using mapped_type = Value;
+  using value_type = typename Container::value_type;
+  using key_compare = Compare;
+  using allocator_type = Allocator;
+  using container_type = Container;
 
   struct value_compare : private Compare {
     bool operator()(const value_type& a, const value_type& b) const {
@@ -983,18 +986,18 @@
     explicit value_compare(const Compare& c) : Compare(c) {}
   };
 
-  typedef typename Container::pointer pointer;
-  typedef typename Container::const_pointer const_pointer;
-  typedef typename Container::reference reference;
-  typedef typename Container::const_reference const_reference;
-  typedef typename Container::iterator iterator;
-  typedef typename Container::const_iterator const_iterator;
-  typedef typename Container::difference_type difference_type;
-  typedef typename Container::size_type size_type;
-  typedef typename Container::reverse_iterator reverse_iterator;
-  typedef typename Container::const_reverse_iterator const_reverse_iterator;
-  typedef detail::DirectMutationGuard<Container, value_compare>
-      direct_mutation_guard;
+  using pointer = typename Container::pointer;
+  using const_pointer = typename Container::const_pointer;
+  using reference = typename Container::reference;
+  using const_reference = typename Container::const_reference;
+  using iterator = typename Container::iterator;
+  using const_iterator = typename Container::const_iterator;
+  using difference_type = typename Container::difference_type;
+  using size_type = typename Container::size_type;
+  using reverse_iterator = typename Container::reverse_iterator;
+  using const_reverse_iterator = typename Container::const_reverse_iterator;
+  using direct_mutation_guard =
+      detail::DirectMutationGuard<Container, value_compare>;
 
   sorted_vector_map() noexcept(
       std::is_nothrow_constructible<EBO, value_compare, Allocator>::value)
@@ -1083,8 +1086,9 @@
                                       value_compare,
                                       Container&&>::value)
       : m_(value_compare(comp), std::move(container)) {
-    assert(detail::is_sorted_unique(
-        m_.cont_.begin(), m_.cont_.end(), value_comp()));
+    assert(
+        detail::is_sorted_unique(
+            m_.cont_.begin(), m_.cont_.end(), value_comp()));
   }
 
   Allocator get_allocator() const { return m_.cont_.get_allocator(); }
@@ -1132,8 +1136,9 @@
     swap(m_.cont_, newContainer);
   }
   void swap_container(sorted_unique_t, Container& newContainer) {
-    assert(detail::is_sorted_unique(
-        newContainer.begin(), newContainer.end(), value_comp()));
+    assert(
+        detail::is_sorted_unique(
+            newContainer.begin(), newContainer.end(), value_comp()));
     using std::swap;
     swap(m_.cont_, newContainer);
   }
diff --git a/folly/folly/container/span.h b/folly/folly/container/span.h
--- a/folly/folly/container/span.h
+++ b/folly/folly/container/span.h
@@ -262,7 +262,7 @@
 
 template <typename R>
 span(R&&) -> span<std::remove_reference_t<
-              iterator_reference_t<decltype(std::begin(std::declval<R&>()))>>>;
+    iterator_reference_t<decltype(std::begin(std::declval<R&>()))>>>;
 
 } // namespace fallback_span
 
@@ -284,8 +284,7 @@
 
 struct span_cast_impl_fn {
   template <
-      template <typename, std::size_t>
-      class Span,
+      template <typename, std::size_t> class Span,
       typename U,
       typename T,
       std::size_t Extent>
diff --git a/folly/folly/container/tape.h b/folly/folly/container/tape.h
--- a/folly/folly/container/tape.h
+++ b/folly/folly/container/tape.h
@@ -83,13 +83,13 @@
  * Construction
  * If you know for a fact that all the elements are fitting into SSO buffer,
  * and you always have complete records (not building) then `tape` does not help
- * you, or can even be a slignt regression.
+ * you, or can even be a slight regression.
  *
  * Otherwise tape can give you good speedups, especially if you need to
  * `push_back` on individual records.
  *
  * Potential future perf improvements.
- * * it is possible to do a tape with one allocation for both metada and
+ * * it is possible to do a tape with one allocation for both metadata and
  *   data (in special cases).
  * * when converting indexes to pointers, compiler has to shift.
  *   For contigious containers we can store offsets in bytes.
@@ -231,10 +231,9 @@
   }
 
   template <typename R>
-  auto push_back(R&& r)
-      -> std::enable_if_t<
-          range_of_scalars<R> &&
-          !std::is_convertible_v<R, const_reference>> // handle \0 separately
+  auto push_back(R&& r) -> std::enable_if_t<
+      range_of_scalars<R> &&
+      !std::is_convertible_v<R, const_reference>> // handle \0 separately
   {
     push_back(std::begin(r), std::end(r));
   }
@@ -265,10 +264,9 @@
   }
 
   template <typename R>
-  auto push_back_unsafe(R&& r)
-      -> std::enable_if_t<
-          range_of_scalars<R> &&
-          !std::is_convertible_v<R, const_reference>> // handle \0 separately
+  auto push_back_unsafe(R&& r) -> std::enable_if_t<
+      range_of_scalars<R> &&
+      !std::is_convertible_v<R, const_reference>> // handle \0 separately
   {
     push_back_unsafe(std::begin(r), std::end(r));
   }
@@ -294,10 +292,9 @@
       -> std::enable_if_t<iterator_of_scalars<I>, iterator>;
 
   template <typename R>
-  auto insert(const_iterator pos, R&& r)
-      -> std::enable_if_t<
-          range_of_scalars<R> && !std::is_convertible_v<R, const_reference>,
-          iterator> {
+  auto insert(const_iterator pos, R&& r) -> std::enable_if_t<
+      range_of_scalars<R> && !std::is_convertible_v<R, const_reference>,
+      iterator> {
     return insert(pos, std::begin(r), std::end(r));
   }
 
diff --git a/folly/folly/container/test/TrackingTypes.h b/folly/folly/container/test/TrackingTypes.h
--- a/folly/folly/container/test/TrackingTypes.h
+++ b/folly/folly/container/test/TrackingTypes.h
@@ -162,7 +162,7 @@
 
 inline std::ostream& operator<<(std::ostream& xo, Counts const& counts) {
   xo << "[";
-  std::string glue = "";
+  std::string glue;
   if (counts.copyConstruct > 0) {
     xo << glue << counts.copyConstruct << " copy";
     glue = ", ";
diff --git a/folly/folly/coro/AsyncGenerator.h b/folly/folly/coro/AsyncGenerator.h
--- a/folly/folly/coro/AsyncGenerator.h
+++ b/folly/folly/coro/AsyncGenerator.h
@@ -25,8 +25,8 @@
 #include <folly/Traits.h>
 #include <folly/Try.h>
 #include <folly/coro/AutoCleanup-fwd.h>
+#include <folly/coro/BasePromise.h>
 #include <folly/coro/Coroutine.h>
-#include <folly/coro/CurrentExecutor.h>
 #include <folly/coro/Invoke.h>
 #include <folly/coro/Result.h>
 #include <folly/coro/ScopeExit.h>
@@ -211,7 +211,7 @@
     typename Reference,
     typename Value = remove_cvref_t<Reference>,
     bool RequiresCleanup = false>
-class FOLLY_NODISCARD AsyncGenerator {
+class [[nodiscard]] AsyncGenerator {
   static_assert(
       std::is_constructible<Value, Reference>::value,
       "AsyncGenerator 'value_type' must be constructible from a 'reference'.");
@@ -224,6 +224,7 @@
   //
   // Future: Implement a `coro/safe` generator wrapper, like
   // `async_closure_gen`.
+  template <safe_alias>
   using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;
 
  private:
@@ -252,7 +253,7 @@
 
   class CleanupSemiAwaitable;
 
-  class FOLLY_NODISCARD CleanupAwaitable {
+  class [[nodiscard]] CleanupAwaitable {
    public:
     bool await_ready() noexcept { return !scopeExit_; }
 
@@ -291,12 +292,13 @@
     folly::Executor::KeepAlive<> executor_;
   };
 
-  class FOLLY_NODISCARD CleanupSemiAwaitable {
+  class [[nodiscard]] CleanupSemiAwaitable {
    public:
     CleanupAwaitable viaIfAsync(Executor::KeepAlive<> executor) noexcept {
       return CleanupAwaitable{scopeExit_, std::move(executor)};
     }
 
+    template <safe_alias>
     using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;
 
    private:
@@ -496,6 +498,7 @@
       return NextSemiAwaitable{std::exchange(awaitable.coro_, {})};
     }
 
+    template <safe_alias>
     using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;
 
    private:
@@ -554,16 +557,27 @@
   coroutine_handle<ScopeExitTaskPromiseBase> scopeExit_;
 };
 
-template <typename Reference, typename Value, bool RequiresCleanup = false>
+enum class AsyncGeneratorPromiseState : std::uint8_t {
+  INVALID,
+  VALUE,
+  EXCEPTION_WRAPPER,
+  DONE,
+};
+
+template <
+    typename Reference,
+    typename Value,
+    bool RequiresCleanup /* = false, in BasePromise.h */>
 class AsyncGeneratorPromise final
-    : public ExtendedCoroutinePromise,
-      BaseAsyncGeneratorPromise<RequiresCleanup> {
+    : public ExtendedCoroutinePromiseCrtp<
+          AsyncGeneratorPromise<Reference, Value, RequiresCleanup>>,
+      BaseAsyncGeneratorPromise<RequiresCleanup>,
+      public BasePromise<AsyncGeneratorPromiseState> {
   class YieldAwaiter {
    public:
     bool await_ready() noexcept { return false; }
-    coroutine_handle<> await_suspend(
-        coroutine_handle<AsyncGeneratorPromise> h) noexcept {
-      AsyncGeneratorPromise& promise = h.promise();
+    coroutine_handle<> await_suspend_promise(
+        AsyncGeneratorPromise& promise) noexcept {
       // Pop AsyncStackFrame first as clearContext() clears the frame state.
       folly::popAsyncStackFrameCallee(promise.getAsyncFrame());
       promise.clearContext();
@@ -574,12 +588,21 @@
       }
       return promise.continuation_.getHandle();
     }
+    coroutine_handle<> await_suspend(
+        coroutine_handle<AsyncGeneratorPromise> h) noexcept {
+      return await_suspend_promise(h.promise());
+    }
     void await_resume() noexcept {}
   };
 
+  using State = AsyncGeneratorPromiseState;
+  State state() const { return tailStorage_; }
+  void setState(State s) { tailStorage_ = s; }
+
  public:
   template <typename... Args>
   AsyncGeneratorPromise(Args&... args) {
+    setState(State::INVALID);
     if constexpr (RequiresCleanup) {
       scheduleAutoCleanupIfNeeded(
           coroutine_handle<AsyncGeneratorPromise>::from_promise(*this),
@@ -588,7 +611,7 @@
   }
 
   ~AsyncGeneratorPromise() {
-    switch (state_) {
+    switch (state()) {
       case State::VALUE:
         folly::coro::detail::deactivate(value_);
         break;
@@ -624,9 +647,9 @@
 
   YieldAwaiter yield_value(Reference&& value) noexcept(
       std::is_nothrow_move_constructible<Reference>::value) {
-    DCHECK(state_ == State::INVALID);
+    DCHECK(state() == State::INVALID);
     folly::coro::detail::activate(value_, static_cast<Reference&&>(value));
-    state_ = State::VALUE;
+    setState(State::VALUE);
     return YieldAwaiter{};
   }
 
@@ -643,17 +666,17 @@
           int> = 0>
   YieldAwaiter yield_value(U&& value) noexcept(
       std::is_nothrow_constructible_v<Reference, U>) {
-    DCHECK(state_ == State::INVALID);
+    DCHECK(state() == State::INVALID);
     folly::coro::detail::activate(value_, static_cast<U&&>(value));
-    state_ = State::VALUE;
+    setState(State::VALUE);
     return {};
   }
 
   YieldAwaiter yield_value(co_error&& error) noexcept {
-    DCHECK(state_ == State::INVALID);
+    DCHECK(state() == State::INVALID);
     folly::coro::detail::activate(
         exceptionWrapper_, std::move(error.exception()));
-    state_ = State::EXCEPTION_WRAPPER;
+    setState(State::EXCEPTION_WRAPPER);
     return {};
   }
 
@@ -687,81 +710,22 @@
     return yield_value(co_error(UsingUninitializedTry{}));
   }
 
+  using BasePromise<AsyncGeneratorPromiseState>::await_transform;
+
   variant_awaitable<YieldAwaiter, ready_awaitable<>> await_transform(
       co_safe_point_t) noexcept {
-    if (cancelToken_.isCancellationRequested()) {
-      return yield_value(co_cancelled);
-    }
-    return ready_awaitable<>{};
+    return do_safe_point<YieldAwaiter>(*this);
   }
 
   void unhandled_exception() noexcept {
-    DCHECK(state_ == State::INVALID);
+    DCHECK(state() == State::INVALID);
     folly::coro::detail::activate(exceptionWrapper_, current_exception());
-    state_ = State::EXCEPTION_WRAPPER;
+    setState(State::EXCEPTION_WRAPPER);
   }
 
   void return_void() noexcept {
-    DCHECK(state_ == State::INVALID);
-    state_ = State::DONE;
-  }
-
-  // FIXME: Much of this class is currently copy-pasted from `TaskPromiseBase`,
-  // Refactor this to use that, so as to avoid `co_await` behavior divergence.
-
-  template <
-      typename Awaitable,
-      std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
-  auto await_transform(Awaitable&& awaitable) {
-    bypassExceptionThrowing_ =
-        bypassExceptionThrowing_ == BypassExceptionThrowing::REQUESTED
-        ? BypassExceptionThrowing::ACTIVE
-        : BypassExceptionThrowing::INACTIVE;
-
-    return folly::coro::co_withAsyncStack(folly::coro::co_viaIfAsync(
-        executor_.get_alias(),
-        folly::coro::co_withCancellation(
-            cancelToken_, static_cast<Awaitable&&>(awaitable))));
-  }
-  template <
-      typename Awaitable,
-      std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
-  auto await_transform(Awaitable awaitable) {
-    bypassExceptionThrowing_ =
-        bypassExceptionThrowing_ == BypassExceptionThrowing::REQUESTED
-        ? BypassExceptionThrowing::ACTIVE
-        : BypassExceptionThrowing::INACTIVE;
-
-    return folly::coro::co_withAsyncStack(folly::coro::co_viaIfAsync(
-        executor_.get_alias(),
-        folly::coro::co_withCancellation(
-            cancelToken_,
-            mustAwaitImmediatelyUnsafeMover(std::move(awaitable))())));
-  }
-
-  template <typename Awaitable>
-  auto await_transform(NothrowAwaitable<Awaitable> awaitable) {
-    bypassExceptionThrowing_ = BypassExceptionThrowing::REQUESTED;
-    return await_transform(
-        mustAwaitImmediatelyUnsafeMover(awaitable.unwrap())());
-  }
-
-  auto await_transform(folly::coro::co_current_executor_t) noexcept {
-    return ready_awaitable<folly::Executor*>{executor_.get()};
-  }
-
-  auto await_transform(folly::coro::co_current_cancellation_token_t) noexcept {
-    return ready_awaitable<const folly::CancellationToken&>{cancelToken_};
-  }
-
-  void setCancellationToken(folly::CancellationToken cancelToken) noexcept {
-    // Only keep the first cancellation token.
-    // ie. the inner-most cancellation scope of the consumer's calling
-    // context.
-    if (!hasCancelTokenOverride_) {
-      cancelToken_ = std::move(cancelToken);
-      hasCancelTokenOverride_ = true;
-    }
+    DCHECK(state() == State::INVALID);
+    setState(State::DONE);
   }
 
   void setExecutor(folly::Executor::KeepAlive<> executor) noexcept {
@@ -774,7 +738,7 @@
   }
 
   bool hasException() const noexcept {
-    return state_ == State::EXCEPTION_WRAPPER;
+    return state() == State::EXCEPTION_WRAPPER;
   }
 
   folly::exception_wrapper& getException() noexcept {
@@ -783,7 +747,7 @@
   }
 
   void throwIfException() {
-    if (state_ == State::EXCEPTION_WRAPPER) {
+    if (state() == State::EXCEPTION_WRAPPER) {
       exceptionWrapper_.get().throw_exception();
     }
   }
@@ -795,33 +759,22 @@
 
   void clearValue() noexcept {
     if (hasValue()) {
-      state_ = State::INVALID;
+      setState(State::INVALID);
       folly::coro::detail::deactivate(value_);
     } else {
-      CHECK(state_ != State::DONE)
+      CHECK(state() != State::DONE)
           << "Using generator after receiving completion.";
-      CHECK(state_ != State::EXCEPTION_WRAPPER)
+      CHECK(state() != State::EXCEPTION_WRAPPER)
           << "Using generator after receiving exception.";
     }
   }
 
-  bool hasValue() const noexcept { return state_ == State::VALUE; }
-
-  folly::AsyncStackFrame& getAsyncFrame() noexcept { return asyncFrame_; }
+  bool hasValue() const noexcept { return state() == State::VALUE; }
 
-  std::pair<ExtendedCoroutineHandle, AsyncStackFrame*> getErrorHandle(
-      exception_wrapper& ex) final {
-    if (bypassExceptionThrowing_ == BypassExceptionThrowing::ACTIVE) {
-      auto yieldAwaiter = yield_value(co_error(std::move(ex)));
-      DCHECK(!yieldAwaiter.await_ready());
-      return {
-          yieldAwaiter.await_suspend(
-              coroutine_handle<AsyncGeneratorPromise>::from_promise(*this)),
-          // yieldAwaiter.await_suspend pops a frame
-          getAsyncFrame().getParentFrame()};
-    }
-    return {
-        coroutine_handle<AsyncGeneratorPromise>::from_promise(*this), nullptr};
+  // Unlike `getErrorHandleUncheckedImpl`, checks the type of `me`.
+  static std::optional<ExtendedCoroutineHandle::ErrorHandle> getErrorHandleImpl(
+      AsyncGeneratorPromise& me, exception_wrapper& ex) {
+    return getErrorHandleUncheckedImpl(me, ex);
   }
 
  private:
@@ -845,29 +798,12 @@
     return std::exchange(p.scopeExit_, scopeExit);
   }
 
-  enum class State : std::uint8_t {
-    INVALID,
-    VALUE,
-    EXCEPTION_WRAPPER,
-    DONE,
-  };
-
-  ExtendedCoroutineHandle continuation_;
-  folly::AsyncStackFrame asyncFrame_;
-  folly::Executor::KeepAlive<> executor_;
-  folly::CancellationToken cancelToken_;
+  // From the base: continuation_, asyncFrame_, executor_, cancelToken_,
+  // hasCancelTokenOverride_, tailStorage_ == state() / setState()
   union {
     ManualLifetime<folly::exception_wrapper> exceptionWrapper_;
     ManualLifetime<Reference> value_;
   };
-  State state_ = State::INVALID;
-  bool hasCancelTokenOverride_ = false;
-
-  enum class BypassExceptionThrowing : uint8_t {
-    INACTIVE,
-    ACTIVE,
-    REQUESTED,
-  } bypassExceptionThrowing_{BypassExceptionThrowing::INACTIVE};
 };
 
 } // namespace detail
diff --git a/folly/folly/coro/AsyncPipe.h b/folly/folly/coro/AsyncPipe.h
--- a/folly/folly/coro/AsyncPipe.h
+++ b/folly/folly/coro/AsyncPipe.h
@@ -242,7 +242,7 @@
     auto parentToken = co_await co_current_cancellation_token;
 
     auto waitResult = co_await co_awaitTry(co_withCancellation(
-        folly::CancellationToken::merge(
+        folly::cancellation_token_merge(
             std::move(parentToken), cancellationToken_),
         semaphore_->co_wait()));
     if (cancellationToken_.isCancellationRequested()) {
diff --git a/folly/folly/coro/AsyncScope.h b/folly/folly/coro/AsyncScope.h
--- a/folly/folly/coro/AsyncScope.h
+++ b/folly/folly/coro/AsyncScope.h
@@ -143,8 +143,8 @@
   /**
    * Asynchronously cleanup all started tasks.
    *
-   * If you have previuosly called add() then you must call cleanup()
-   * and wait for the retuned future to complete before the AsyncScope
+   * If you have previously called add() then you must call cleanup()
+   * and wait for the returned future to complete before the AsyncScope
    * object destructs.
    */
   SemiFuture<Unit> cleanup() noexcept;
@@ -294,10 +294,10 @@
       : cancellationToken_(cancellationSource_.getToken()),
         scope_(throwOnJoin) {}
   explicit CancellableAsyncScope(CancellationToken&& token)
-      : cancellationToken_(CancellationToken::merge(
+      : cancellationToken_(cancellation_token_merge(
             cancellationSource_.getToken(), std::move(token))) {}
   CancellableAsyncScope(CancellationToken&& token, bool throwOnJoin)
-      : cancellationToken_(CancellationToken::merge(
+      : cancellationToken_(cancellation_token_merge(
             cancellationSource_.getToken(), std::move(token))),
         scope_(throwOnJoin) {}
 
@@ -326,7 +326,7 @@
       void* returnAddress = nullptr) {
     scope_.add(
         co_withCancellation(
-            token ? CancellationToken::merge(*token, cancellationToken_)
+            token ? cancellation_token_merge(*token, cancellationToken_)
                   : cancellationToken_,
             static_cast<Awaitable&&>(awaitable)),
         returnAddress ? returnAddress : FOLLY_ASYNC_STACK_RETURN_ADDRESS());
@@ -340,7 +340,7 @@
       source_location sourceLocation = source_location::current()) {
     scope_.addWithSourceLoc(
         co_withCancellation(
-            token ? CancellationToken::merge(*token, cancellationToken_)
+            token ? cancellation_token_merge(*token, cancellationToken_)
                   : cancellationToken_,
             static_cast<Awaitable&&>(awaitable)),
         returnAddress ? returnAddress : FOLLY_ASYNC_STACK_RETURN_ADDRESS(),
@@ -363,12 +363,18 @@
    * AsyncScope. The task will be provided a cancellation token to respond to
    * cancelAndJoinAsync() in the future.
    *
+   * An additional cancellation token may be passed in to apply to the
+   * awaitable; it will be merged with the internal token.
+   *
    * Note that cancellation is cooperative, your task must handle cancellation
    * in order to have any effect.
    */
   template <class T>
-  folly::coro::Task<void> co_schedule(folly::coro::Task<T>&& task) {
-    add(co_withExecutor(co_await co_current_executor, std::move(task)));
+  folly::coro::Task<void> co_schedule(
+      folly::coro::Task<T>&& task,
+      std::optional<CancellationToken> token = std::nullopt) {
+    add(co_withExecutor(co_await co_current_executor, std::move(task)),
+        std::move(token));
   }
 
   /**
diff --git a/folly/folly/coro/AsyncStack.h b/folly/folly/coro/AsyncStack.h
--- a/folly/folly/coro/AsyncStack.h
+++ b/folly/folly/coro/AsyncStack.h
@@ -30,7 +30,7 @@
 class AsyncStackTraceAwaitable {
   class Awaiter {
    public:
-    bool await_ready() const { return false; }
+    bool await_ready() const noexcept { return false; }
 
     template <typename Promise>
     bool await_suspend(coroutine_handle<Promise> h) noexcept {
diff --git a/folly/folly/coro/AwaitImmediately.h b/folly/folly/coro/AwaitImmediately.h
deleted file mode 100644
--- a/folly/folly/coro/AwaitImmediately.h
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <type_traits>
-
-#include <folly/Utility.h>
-
-#if FOLLY_HAS_COROUTINES
-
-namespace folly::coro {
-
-// ## What are immediately-awaitable types, and how should I handle them?
-//
-// When `must_await_immediately_v<A> == true`, this indicates that the
-// awaitable or semiawaitable `A` should be `co_await`ed in the full-expression
-// that created it.  For an example, see `NowTask.h`.  Using immediate
-// awaitables reduces the risk of lifetime bugs.
-//
-// To create a new immediately-awaitable type, follow this protocol:
-//   - Derive from `public AddMustAwaitImmediately<YourBase>`.
-//   - Implement `getUnsafeMover(ForMustAwaitImmediately) && noexcept`, but
-//     first read the ENTIRE docblock of `mustAwaitImmediatelyUnsafeMover()`
-//     with the notes on object slicing and `noexcept` behavior.
-//
-// To handle immediately-awaitables, `folly::coro` APIs must follow these rules:
-//
-//  (1) NEVER expose non-static member functions for actions that consume the
-//      (semi)awaitable.  For example, `task.scheduleOn()` was removed in favor
-//      of `co_withExecutor()`, `.start()` and `.semi()` are unsafe, etc.
-//
-//      INSTEAD: Use static member functions, or ADL CPOs, which take the
-//      (semi)awaitable by-value.
-//
-//  (2) If your API must also support await-by-reference for types like
-//      `Baton`, then you must bifurcate the API on the return value of
-//      `must_await_immediately_v`.  Grep for examples.
-//         - `true`: Pass-by-value
-//         - `false`: Pass-by-forwarding reference
-//
-//  (3) Immediately-awaitable types are immovable, but you may need to move
-//      them internally in your library implementation.  For this, you can use
-//      the callable from `mustAwaitImmediatelyUnsafeMover(std::move(t))`.  For
-//      example, you can move the mover into another coroutine, and then invoke
-//      its `operator()` to reconstitute the (semi)awaitable.  Most often, you
-//      will invoke the mover inline.
-//
-//      DANGER: You should NOT use this to move immovable types outside of
-//      `folly::coro` library internals, where the lifetime safety is assured
-//      via pass-by-value from (1) or (2).
-//
-// Caveat: If you encounter a public `folly::coro` API that is does not yet
-// handle immediately-awaitable types, and simply takes the awaitable by `&&`,
-// please fix it via one of these paths:
-//   - If possible, switch it to take all (semi)awaitables by-value.
-//   - If not, branch the API as in (2) above.
-//   - Ask for help in the Coroutines group.
-
-namespace detail {
-
-template <typename T>
-using must_await_immediately_of_ =
-    typename T::folly_private_must_await_immediately_t;
-
-template <typename Void, typename T>
-struct must_await_immediately_ {
-  static_assert(
-      require_sizeof<T>, "`must_await_immediately_t` on incomplete type");
-  using type = std::false_type;
-};
-
-template <>
-struct must_await_immediately_<void, void> {
-  using type = std::false_type;
-};
-
-template <typename T>
-struct must_await_immediately_<void_t<must_await_immediately_of_<T>>, T> {
-  // We _could_ do an "is `T` immovable" check, but the cost/benefit seems low.
-  // That would only guard against a users wrongly adding this to their type:
-  //   folly_private_must_await_immediately_t = std::true_type
-  // instead of inheriting from `AddMustAwaitImmediately<>`.
-  using type = must_await_immediately_of_<T>;
-};
-
-} // namespace detail
-
-template <typename T>
-using must_await_immediately_t =
-    typename detail::must_await_immediately_<void, T>::type;
-
-template <typename T>
-inline constexpr bool must_await_immediately_v =
-    must_await_immediately_t<T>::value;
-
-// To make a (semi)awaitable immediate, have it publicly inherit from
-// `AddMustAwaitImmediately<InnerSemiAwaitable>`.  It is templated on the
-// "inner type" so that all bases are distinct in a tower-of-wrappers, like
-// `TryAwaitable<NowTask<T>>`.  Repeating a base would break the empty basea
-// optimization (EBO).
-template <typename InnerT>
-struct AddMustAwaitImmediately : public InnerT {
-  using folly_private_must_await_immediately_t = std::true_type;
-
-  using InnerT::InnerT;
-
-  // Avoiding `NonCopyableNonMovable` to avoid breaking EBO.
-  ~AddMustAwaitImmediately() = default;
-  AddMustAwaitImmediately(AddMustAwaitImmediately&&) = delete;
-  AddMustAwaitImmediately& operator=(AddMustAwaitImmediately&&) = delete;
-  AddMustAwaitImmediately(const AddMustAwaitImmediately&) = delete;
-  AddMustAwaitImmediately& operator=(const AddMustAwaitImmediately&) = delete;
-};
-
-// See `mustAwaitImmediatelyUnsafeMover()` for the docs.  In short, for an
-// `Outer` that is immovable, this stores an unwrapped `Inner` (semi)awaitable
-// that can reconstitute `Outer` on `operator()`.
-//
-// DANGER: Before returning this class from your `getUnsafeMover()`, you must
-// review "A note on object slicing" and make sure your usage isn't affected.
-template <typename Outer, typename InnerMover>
-class MustAwaitImmediatelyUnsafeMover {
- private:
-  InnerMover mover_;
-
- public:
-  // `Outer*` is just for type deduction and should be `nullptr`.
-  MustAwaitImmediatelyUnsafeMover(Outer*, InnerMover m) noexcept
-      : mover_(std::move(m)) {
-    // See mustAwaitImmediatelyUnsafeMover docblock
-    static_assert(std::is_nothrow_move_constructible_v<InnerMover>);
-    // See "A note on object slicing" below
-    static_assert(
-        sizeof(Outer) == sizeof(decltype(FOLLY_DECLVAL(InnerMover)())));
-  }
-  Outer operator()() && noexcept {
-    // See mustAwaitImmediatelyUnsafeMover docblock
-    static_assert(noexcept(Outer{std::move(mover_)()}));
-    return Outer{std::move(mover_)()};
-  }
-};
-
-// Analog of `MustAwaitImmediatelyUnsafeMover` for movable (semi)awaitables.
-template <typename T>
-struct NoOpMover {
- private:
-  T t_;
-
- public:
-  explicit NoOpMover(T t) noexcept : t_(std::move(t)) {
-    // See mustAwaitImmediatelyUnsafeMover docblock
-    static_assert(std::is_nothrow_move_constructible_v<T>);
-  }
-  T operator()() && noexcept { return std::move(t_); }
-};
-
-// Overload tag / passkey for the customizable method `getUnsafeMover`.
-struct ForMustAwaitImmediately {};
-
-namespace detail {
-template <typename T>
-using unsafe_mover_for_must_await_immediately_t =
-    decltype(FOLLY_DECLVAL(T).getUnsafeMover(ForMustAwaitImmediately{}));
-}
-
-// After taking an immediately-(semi)awaitable by-value, this lets
-// `folly::coro` libraries move it internally.  They MUST make sure not to let
-// the awaitable be used outside of its original full-expression.
-//
-// This wraps `getUnsafeMover` for types that implement it, and provides
-// a no-op fallback for those that don't. Required semantics:
-//
-//  - It's a destructive operation -- hence the r-value qualifier
-//  - It takes ownership of the internals of `awaitable`.
-//  - It returns a mover value (never a reference), whose `operator() &&` is a
-//    single-use operation that returns a new awaitable equivalent to the
-//    original `awaitable` that was passed in & moved out.
-//
-// ## A note on object slicing -- for `getUnsafeMover` implementations
-//
-// The default `getUnsafeMover()` implementations return `NoOpMover` or
-// `MustAwaitImmediatelyUnsafeMover`.  The net effect is that the mover
-// stores an unwrapped, inner type that is movable (like `Task`), and
-// its `operator()` reconstitues the original "outer" type.
-//
-// This is fine for type-only wrappers.  But, object slicing is a danger for
-// wrappers that affect the object's lifetime management or layout. For example:
-//   - If "outer" adds a new member, this would be discarded.  The current
-//     `getUnsafeMover`s compare before/after `sizeof`.  Do that in new ones!
-//   - If the wrapper customizes destruction, move, copy, or assignment, then
-//     the wrapping/unwrapping will cause the custom logic will run at an
-//     unexpected time.  Such wrapper types MUST customize `getUnsafeMover` to
-//     return a custom mover that handles this correctly.
-//
-// ## A note on `noexcept` discipline
-//
-// This `static_assert`s that constructing **and** using a mover is `noexcept`.
-// This could, of course, be relaxed via `noexcept(noexcept(...))` logic, but
-// IMO no `folly::coro` awaitables SHOULD have throwing move ctors, so this
-// requirement is Actually Fine (until proven otherwise).
-template <
-    typename Awaitable,
-    typename DetectRes = detected_or<
-        NoOpMover<Awaitable>,
-        detail::unsafe_mover_for_must_await_immediately_t,
-        Awaitable>>
-// CAREFUL: Passing by `&&` can violate the immediately-awaitable restriction!
-typename DetectRes::type mustAwaitImmediatelyUnsafeMover(
-    Awaitable&& awaitable) noexcept {
-  static_assert(noexcept(FOLLY_DECLVAL(typename DetectRes::type&&)()));
-  if constexpr (DetectRes::value_t::value) {
-    static_assert(noexcept(static_cast<Awaitable&&>(awaitable).getUnsafeMover(
-        ForMustAwaitImmediately{})));
-    return static_cast<Awaitable&&>(awaitable).getUnsafeMover(
-        ForMustAwaitImmediately{});
-  } else {
-    static_assert(
-        std::is_nothrow_constructible_v<NoOpMover<Awaitable>, Awaitable&&>);
-    return NoOpMover<Awaitable>{static_cast<Awaitable&&>(awaitable)};
-  }
-}
-
-} // namespace folly::coro
-
-#endif
diff --git a/folly/folly/coro/AwaitResult.h b/folly/folly/coro/AwaitResult.h
deleted file mode 100644
--- a/folly/folly/coro/AwaitResult.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/coro/ViaIfAsync.h>
-#include <folly/result/try.h>
-
-#if FOLLY_HAS_RESULT
-
-namespace folly::coro {
-
-/// `co_await_result` is the `result<T>` analog of the older `co_awaitTry`.
-///
-/// In a `folly::coro` async coroutine, use `co_await_result` like so:
-///
-///   result<int> res = co_await co_await_result(taskReturningInt());
-///   if (auto* ex = get_exception<MyError>(res)) {
-///     /* handle ex */
-///   } else {
-///     sum += co_await co_ready(res); // efficiently propagate unhandled error
-///   }
-///
-/// Contrast that with related async coro vocabulary:
-///  - `co_yield co_result(r)` from `Result.h` -- propagate `result<T>` or
-///    `Try<T>` to the awaiter of the current coro.
-///  - `auto& v = co_await co_ready(r)` from `Ready.h` -- given a `result<T>`,
-///    unpack the value, or propagate any error to our awaiter.
-///
-/// The purpose of `co_await_result` is to handle errors from a child task via
-/// `result<T>`, rather than through `try {} catch {}`.  Some reasons to do so:
-///   - Your error-handling APIs (logging, retry, etc) use `result<T>`.
-///   - You wish to avoid the ~microsecond cost of thrown exceptions,
-///     applicable only when your error path is hot, and the child uses
-///     `co_yield` instead of `throw` to propagate exceptions.
-
-namespace detail {
-
-template <typename Awaiter>
-using detect_await_resume_result =
-    decltype(FOLLY_DECLVAL(Awaiter).await_resume_result());
-
-template <typename Awaiter>
-constexpr bool is_awaiter_result =
-    is_detected_v<detect_await_resume_result, Awaiter>;
-
-template <typename Awaitable>
-constexpr bool is_awaitable_result =
-    is_awaiter_result<awaiter_type_t<Awaitable>>;
-
-// On the happy path, this uses the dedicated `await_resume_result()` protocol,
-// if it's supported by the awaiter.
-//
-// As fallback, this reuses the `await_resume_try()` machinery in the hope that
-// the compiler will be able to optimize away the `Try` -> `result` conversion.
-//
-// The reasons to support the dedicated protocol are (1) better semantics, and
-// (2) a data flow that's easier for the compiler to optimize.  Specifically:
-//
-//   - `await_resume_result()` cleanly handles `Task<V&>`, whereas `Try`
-//     doesn't support storing references, and the caller of `co_awaitTry` has
-//     to deal with `Try<std::reference_wrapper<V>>`.  See the test in
-//     `TaskOfLvalueReferenceAsTry`.
-//
-//   - `await_resume_result()` implementations can explicitly avoid the "empty
-//     `Try`" pitfall, which is something that gets converted to a
-//     `UsingUninitializedTry` error by the `try_to_result()` fallback.
-//
-//   - Falling back to `await_resume_try()` can incur an extra move-copy, which
-//     may not always optimize away.
-template <typename Awaitable>
-class ResultAwaiter {
- private:
-  static_assert(is_awaitable_try<Awaitable> || is_awaitable_result<Awaitable>);
-
-  using Awaiter = awaiter_type_t<Awaitable>;
-  Awaiter awaiter_;
-
- public:
-  explicit ResultAwaiter(Awaitable&& awaiter)
-      : awaiter_(get_awaiter(static_cast<Awaitable&&>(awaiter))) {}
-
-  // clang-format off
-  auto await_ready() FOLLY_DETAIL_FORWARD_BODY(awaiter_.await_ready())
-
-  template <typename Promise>
-  auto await_suspend(coroutine_handle<Promise> coro)
-      FOLLY_DETAIL_FORWARD_BODY(awaiter_.await_suspend(coro))
-      // clang-format on
-
-      template <
-          typename Awaiter2 = Awaiter,
-          typename Result =
-              decltype(FOLLY_DECLVAL(Awaiter2&).await_resume_result())>
-      Result await_resume() noexcept(noexcept(awaiter_.await_resume_result())) {
-    return awaiter_.await_resume_result();
-  }
-
-  template <
-      typename Awaiter2 = Awaiter,
-      typename Result =
-          decltype(try_to_result(FOLLY_DECLVAL(Awaiter2&).await_resume_try()))>
-  Result await_resume() noexcept(
-      noexcept(try_to_result(awaiter_.await_resume_try())))
-    requires(!is_awaitable_result<Awaitable>)
-  {
-    return try_to_result(awaiter_.await_resume_try());
-  }
-};
-
-template <typename T>
-class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] ResultAwaitable
-    : public CommutativeWrapperAwaitable<ResultAwaitable, T> {
- public:
-  using CommutativeWrapperAwaitable<ResultAwaitable, T>::
-      CommutativeWrapperAwaitable;
-
-  template <
-      typename Self,
-      std::enable_if_t<
-          std::is_same_v<remove_cvref_t<Self>, ResultAwaitable>,
-          int> = 0,
-      typename T2 = like_t<Self, T>,
-      std::enable_if_t<is_awaitable_v<T2>, int> = 0>
-  friend ResultAwaiter<T2> operator co_await(Self && self) {
-    return ResultAwaiter<T2>{static_cast<Self&&>(self).inner_};
-  }
-
-  using folly_private_noexcept_awaitable_t = std::true_type;
-};
-
-} // namespace detail
-
-// IMPORTANT: If you need an `Awaitable&&` overload, you must bifurcate this
-// API on `must_await_immediately_v`, see `co_awaitTry` for an example.
-template <typename Awaitable>
-detail::ResultAwaitable<Awaitable> co_await_result(
-    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {
-  return detail::ResultAwaitable<Awaitable>{
-      mustAwaitImmediatelyUnsafeMover(std::move(awaitable))()};
-}
-
-} // namespace folly::coro
-
-#endif
diff --git a/folly/folly/coro/BasePromise.h b/folly/folly/coro/BasePromise.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/coro/BasePromise.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_COROUTINES
+#include <folly/coro/CurrentExecutor.h>
+#include <folly/coro/Error.h>
+#include <folly/coro/Nothrow.h>
+#include <folly/coro/ValueOrError.h>
+
+/// Coroutine promise implementation shared between tasks and async generators.
+/// Only the `await_transform` signatures here are of interest to end-users.
+
+#if FOLLY_HAS_COROUTINES
+
+namespace folly::coro::detail {
+
+class TaskPromiseBase;
+
+template <typename Reference, typename Value, bool RequiresCleanup = false>
+class AsyncGeneratorPromise;
+
+template <typename TailStorage = Unit>
+class BasePromise {
+ protected:
+  // The destructor is protected to alert future authors -- this is a TIGHTLY
+  // COUPLED DETAIL of task & async generator, not an easily reusable
+  // component.  For example, it relies on correctly specified
+  // `await_transform` behavior, a correct definition of the `getErrorHandle`
+  // protocol, and its correct usage in `await_suspend`.
+  friend class TaskPromiseBase;
+  template <typename, typename, bool>
+  friend class AsyncGeneratorPromise;
+  ~BasePromise() = default;
+
+  ExtendedCoroutineHandle continuation_;
+  folly::AsyncStackFrame asyncFrame_;
+  folly::Executor::KeepAlive<> executor_;
+  folly::CancellationToken cancelToken_;
+  bool hasCancelTokenOverride_ = false;
+  BypassExceptionThrowing bypassThrowing_;
+  // Let derived classes pack data in one word with the prior 2 members
+  // This could perhaps be avoided by using bitfields...
+  TailStorage tailStorage_;
+
+  // Implementation of `await_transform(co_safe_point_t)`
+  template <typename Awaiter, typename Promise>
+  variant_awaitable<Awaiter, ready_awaitable<>> do_safe_point(
+      Promise& promise) noexcept {
+    if (cancelToken_.isCancellationRequested()) {
+      return promise.yield_value(co_stopped_may_throw);
+    }
+    return ready_awaitable<>{};
+  }
+
+  // Async generator & task specialize this to check the type of Promise
+  template <typename Promise>
+  static std::optional<ExtendedCoroutineHandle::ErrorHandle>
+  getErrorHandleUncheckedImpl(Promise& me, exception_wrapper& ex) {
+    if (me.bypassThrowing_.shouldBypassFor(ex)) {
+      auto finalAwaiter = me.yield_value(co_error(std::move(ex)));
+      DCHECK(!finalAwaiter.await_ready());
+      return ExtendedCoroutineHandle::ErrorHandle{
+          finalAwaiter.await_suspend_promise(me),
+          // finalAwaiter.await_suspend pops a frame
+          me.getAsyncFrame().getParentFrame()};
+    }
+    return std::nullopt;
+  }
+
+ public:
+  template <
+      typename Awaitable,
+      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
+  auto await_transform(Awaitable&& awaitable) {
+    bypassThrowing_.maybeActivate<Awaitable>();
+    return folly::coro::co_withAsyncStack(
+        folly::coro::co_viaIfAsync(
+            executor_.get_alias(),
+            folly::coro::co_withCancellation(
+                cancelToken_, static_cast<Awaitable&&>(awaitable))));
+  }
+  template <
+      typename Awaitable,
+      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
+  auto await_transform(Awaitable awaitable) {
+    bypassThrowing_.maybeActivate<Awaitable>();
+    return folly::coro::co_withAsyncStack(
+        folly::coro::co_viaIfAsync(
+            executor_.get_alias(),
+            folly::coro::co_withCancellation(
+                cancelToken_,
+                folly::ext::must_use_immediately_unsafe_mover(
+                    std::move(awaitable))())));
+  }
+
+  template <typename Awaitable>
+  auto await_transform(NothrowAwaitable<Awaitable> awaitable) {
+    bypassThrowing_.requestDueToNothrow<Awaitable>();
+    return await_transform(
+        folly::ext::must_use_immediately_unsafe_mover(awaitable.unwrap())());
+  }
+
+  template <typename Awaitable>
+  auto await_transform(ValueOrError<Awaitable> awaitable) {
+    bypassThrowing_.template requestDueToValueOrError<Awaitable>();
+    return await_transform(std::move(awaitable).toValueOrErrorImpl());
+  }
+
+  auto await_transform(co_current_executor_t) noexcept {
+    return ready_awaitable<folly::Executor*>{executor_.get()};
+  }
+
+  auto await_transform(co_current_cancellation_token_t) noexcept {
+    return ready_awaitable<const folly::CancellationToken&>{cancelToken_};
+  }
+
+  // End-users can do this via `co_withCancellation`.
+  void setCancellationToken(folly::CancellationToken cancelToken) noexcept {
+    // Only keep the first cancellation token.  This is usually the inner-most
+    // cancellation scope of the consumer's calling context.
+    if (!hasCancelTokenOverride_) {
+      cancelToken_ = std::move(cancelToken);
+      hasCancelTokenOverride_ = true;
+    }
+  }
+
+  folly::AsyncStackFrame& getAsyncFrame() noexcept { return asyncFrame_; }
+};
+
+} // namespace folly::coro::detail
+
+#endif // FOLLY_HAS_COROUTINES
diff --git a/folly/folly/coro/BlockingWait.h b/folly/folly/coro/BlockingWait.h
--- a/folly/folly/coro/BlockingWait.h
+++ b/folly/folly/coro/BlockingWait.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <folly/Try.h>
-#include <folly/coro/AwaitImmediately.h>
 #include <folly/coro/Coroutine.h>
 #include <folly/coro/Task.h>
 #include <folly/coro/Traits.h>
@@ -26,11 +25,14 @@
 #include <folly/coro/detail/Malloc.h>
 #include <folly/coro/detail/Traits.h>
 #include <folly/executors/ManualExecutor.h>
+#include <folly/executors/SequencedExecutor.h>
 #include <folly/fibers/Baton.h>
+#include <folly/lang/MustUseImmediately.h>
 #include <folly/synchronization/Baton.h>
 #include <folly/tracing/AsyncStack.h>
 
 #include <cassert>
+#include <deque>
 #include <exception>
 #include <type_traits>
 #include <utility>
@@ -271,7 +273,9 @@
   co_yield co_await static_cast<Awaitable&&>(awaitable);
 }
 
-class BlockingWaitExecutor final : public folly::DrivableExecutor {
+class BlockingWaitExecutor final
+    : public folly::DrivableExecutor,
+      public SequencedExecutor {
  public:
   ~BlockingWaitExecutor() override {
     while (keepAliveCount_.load() > 0) {
@@ -284,7 +288,8 @@
     {
       auto wQueue = queue_.wlock();
       empty = wQueue->empty();
-      wQueue->push_back(std::move(func));
+      wQueue->emplace_back(
+          std::move(func), folly::RequestContext::saveContext());
     }
     if (empty) {
       baton_.post();
@@ -296,10 +301,12 @@
     baton_.reset();
 
     folly::fibers::runInMainContext([&]() {
-      std::vector<Func> funcs;
-      queue_.swap(funcs);
-      for (auto& func : funcs) {
-        std::exchange(func, nullptr)();
+      std::deque<BlockingWaitTaskInfo> infos;
+      queue_.swap(infos);
+      RequestContextSaverScopeGuard guard;
+      for (auto& info : infos) {
+        folly::RequestContext::setContext(std::move(info.rctx));
+        std::exchange(info.func, nullptr)();
       }
     });
   }
@@ -331,7 +338,14 @@
         std::memory_order_relaxed));
   }
 
-  folly::Synchronized<std::vector<Func>> queue_;
+  struct BlockingWaitTaskInfo {
+    Func func;
+    std::shared_ptr<folly::RequestContext> rctx;
+    BlockingWaitTaskInfo(Func f, std::shared_ptr<folly::RequestContext> r)
+        : func(std::move(f)), rctx(std::move(r)) {}
+  };
+
+  folly::Synchronized<std::deque<BlockingWaitTaskInfo>> queue_;
   fibers::Baton baton_;
 
   std::atomic<ssize_t> keepAliveCount_{0};
@@ -371,7 +385,9 @@
 
   template <
       typename SemiAwaitable,
-      std::enable_if_t<!must_await_immediately_v<SemiAwaitable>, int> = 0>
+      std::enable_if_t<
+          !folly::ext::must_use_immediately_v<SemiAwaitable>,
+          int> = 0>
   FOLLY_NOINLINE auto operator()(
       SemiAwaitable&& awaitable, folly::DrivableExecutor* executor) const
       -> detail::decay_rvalue_reference_t<semi_await_result_t<SemiAwaitable>> {
@@ -393,7 +409,8 @@
   }
   template <
       typename SemiAwaitable,
-      std::enable_if_t<must_await_immediately_v<SemiAwaitable>, int> = 0>
+      std::enable_if_t<folly::ext::must_use_immediately_v<SemiAwaitable>, int> =
+          0>
   FOLLY_NOINLINE auto operator()(
       SemiAwaitable awaitable, folly::DrivableExecutor* executor) const
       -> detail::decay_rvalue_reference_t<semi_await_result_t<SemiAwaitable>> {
@@ -410,14 +427,17 @@
         detail::makeRefBlockingWaitTask(
             folly::coro::co_viaIfAsync(
                 folly::getKeepAliveToken(executor),
-                mustAwaitImmediatelyUnsafeMover(std::move(awaitable))()))
+                folly::ext::must_use_immediately_unsafe_mover(
+                    std::move(awaitable))()))
             .getVia(executor, frame));
   }
 
   template <
       typename SemiAwaitable,
       std::enable_if_t<!is_awaitable_v<SemiAwaitable>, int> = 0,
-      std::enable_if_t<!must_await_immediately_v<SemiAwaitable>, int> = 0>
+      std::enable_if_t<
+          !folly::ext::must_use_immediately_v<SemiAwaitable>,
+          int> = 0>
   auto operator()(SemiAwaitable&& awaitable) const
       -> detail::decay_rvalue_reference_t<semi_await_result_t<SemiAwaitable>> {
     std::exception_ptr eptr;
@@ -434,7 +454,8 @@
   template <
       typename SemiAwaitable,
       std::enable_if_t<!is_awaitable_v<SemiAwaitable>, int> = 0,
-      std::enable_if_t<must_await_immediately_v<SemiAwaitable>, int> = 0>
+      std::enable_if_t<folly::ext::must_use_immediately_v<SemiAwaitable>, int> =
+          0>
   auto operator()(SemiAwaitable awaitable) const
       -> detail::decay_rvalue_reference_t<semi_await_result_t<SemiAwaitable>> {
     std::exception_ptr eptr;
@@ -442,7 +463,9 @@
       detail::BlockingWaitExecutor executor;
       try {
         return operator()(
-            mustAwaitImmediatelyUnsafeMover(std::move(awaitable))(), &executor);
+            folly::ext::must_use_immediately_unsafe_mover(
+                std::move(awaitable))(),
+            &executor);
       } catch (...) {
         eptr = current_exception();
       }
diff --git a/folly/folly/coro/Collect-inl.h b/folly/folly/coro/Collect-inl.h
--- a/folly/folly/coro/Collect-inl.h
+++ b/folly/folly/coro/Collect-inl.h
@@ -84,10 +84,11 @@
               cancelToken, static_cast<SemiAwaitableMover&&>(mover)()));
       result.emplace();
     } else {
-      result.emplace(co_await co_viaIfAsync(
-          std::move(executor),
-          co_withCancellation(
-              cancelToken, static_cast<SemiAwaitableMover&&>(mover)())));
+      result.emplace(
+          co_await co_viaIfAsync(
+              std::move(executor),
+              co_withCancellation(
+                  cancelToken, static_cast<SemiAwaitableMover&&>(mover)())));
     }
   } catch (...) {
     result.emplaceException(current_exception());
@@ -158,7 +159,7 @@
 Ret collectAllImpl(
     tag_t<Ret, SemiAwaitables...>,
     std::index_sequence<Indices...>,
-    // `semiFns()` is the immovable, must-await-immediately `SemiAwaitable`
+    // `semiFns()` is the immovable, must-use-immediately `SemiAwaitable`
     SemiFns... semiFns) {
   if constexpr (sizeof...(SemiAwaitables) == 0) {
     co_return std::tuple<>{};
@@ -169,7 +170,7 @@
 
     const CancellationSource cancelSource;
     const CancellationToken cancelToken =
-        CancellationToken::merge(parentCancelToken, cancelSource.getToken());
+        cancellation_token_merge(parentCancelToken, cancelSource.getToken());
 
     exception_wrapper firstException;
 
@@ -184,10 +185,11 @@
                   cancelToken, static_cast<decltype(fn)>(fn)()));
           result.emplace();
         } else {
-          result.emplace(co_await co_viaIfAsync(
-              executor.get_alias(),
-              co_withCancellation(
-                  cancelToken, static_cast<decltype(fn)>(fn)())));
+          result.emplace(
+              co_await co_viaIfAsync(
+                  executor.get_alias(),
+                  co_withCancellation(
+                      cancelToken, static_cast<decltype(fn)>(fn)())));
         }
       } catch (...) {
         if (!cancelSource.requestCancellation()) {
@@ -324,24 +326,25 @@
       co_await co_current_cancellation_token;
   const CancellationSource cancelSource;
   const CancellationToken cancelToken =
-      CancellationToken::merge(parentCancelToken, cancelSource.getToken());
+      cancellation_token_merge(parentCancelToken, cancelSource.getToken());
 
   std::pair<std::size_t, folly::Try<collect_any_component_t<SemiAwaitables...>>>
       firstCompletion;
   firstCompletion.first = size_t(-1);
-  co_await folly::coro::collectAll(folly::coro::co_withCancellation(
-      cancelToken,
-      folly::coro::co_invoke(
-          [&, aw = static_cast<SemiAwaitables&&>(awaitables)]() mutable
-          -> folly::coro::Task<void> {
-            auto result = co_await folly::coro::co_awaitTry(
-                static_cast<SemiAwaitables&&>(aw));
-            if (!cancelSource.requestCancellation()) {
-              // This is first entity to request cancellation.
-              firstCompletion.first = Indices;
-              firstCompletion.second = std::move(result);
-            }
-          }))...);
+  co_await folly::coro::collectAll(
+      folly::coro::co_withCancellation(
+          cancelToken,
+          folly::coro::co_invoke(
+              [&, aw = static_cast<SemiAwaitables&&>(awaitables)]() mutable
+                  -> folly::coro::Task<void> {
+                auto result = co_await folly::coro::co_awaitTry(
+                    static_cast<SemiAwaitables&&>(aw));
+                if (!cancelSource.requestCancellation()) {
+                  // This is first entity to request cancellation.
+                  firstCompletion.first = Indices;
+                  firstCompletion.second = std::move(result);
+                }
+              }))...);
 
   co_return firstCompletion;
 }
@@ -356,24 +359,26 @@
       co_await co_current_cancellation_token;
   const CancellationSource cancelSource;
   const CancellationToken cancelToken =
-      CancellationToken::merge(parentCancelToken, cancelSource.getToken());
+      cancellation_token_merge(parentCancelToken, cancelSource.getToken());
 
   constexpr std::size_t nAwaitables = sizeof...(SemiAwaitables);
   std::atomic<std::size_t> nAwaited = 1;
   std::pair<std::size_t, folly::Try<collect_any_component_t<SemiAwaitables...>>>
       firstValueOrLastException;
   firstValueOrLastException.first = std::numeric_limits<size_t>::max();
-  co_await folly::coro::collectAll(folly::coro::co_withCancellation(
-      cancelToken, [&]() -> folly::coro::Task<void> {
-        auto result = co_await folly::coro::co_awaitTry(
-            std::forward<SemiAwaitables>(awaitables));
-        if ((result.hasValue() ||
-             nAwaited.fetch_add(1, std::memory_order_relaxed) == nAwaitables) &&
-            !cancelSource.requestCancellation()) {
-          firstValueOrLastException.first = Indices;
-          firstValueOrLastException.second = std::move(result);
-        }
-      }())...);
+  co_await folly::coro::collectAll(
+      folly::coro::co_withCancellation(
+          cancelToken, [&]() -> folly::coro::Task<void> {
+            auto result = co_await folly::coro::co_awaitTry(
+                std::forward<SemiAwaitables>(awaitables));
+            if ((result.hasValue() ||
+                 nAwaited.fetch_add(1, std::memory_order_relaxed) ==
+                     nAwaitables) &&
+                !cancelSource.requestCancellation()) {
+              firstValueOrLastException.first = Indices;
+              firstValueOrLastException.second = std::move(result);
+            }
+          }())...);
 
   co_return firstValueOrLastException;
 }
@@ -384,17 +389,18 @@
     -> folly::coro::Task<
         std::tuple<collect_all_try_component_t<SemiAwaitables>...>> {
   const CancellationSource cancelSource;
-  const CancellationToken cancelToken = CancellationToken::merge(
+  const CancellationToken cancelToken = cancellation_token_merge(
       co_await co_current_cancellation_token, cancelSource.getToken());
 
   std::tuple<collect_all_try_component_t<SemiAwaitables>...> results;
-  co_await folly::coro::collectAll(folly::coro::co_withCancellation(
-      cancelToken, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {
-        auto result = co_await folly::coro::co_awaitTry(
-            std::forward<SemiAwaitables>(awaitables));
-        cancelSource.requestCancellation();
-        std::get<Indices>(results) = std::move(result);
-      }))...);
+  co_await folly::coro::collectAll(
+      folly::coro::co_withCancellation(
+          cancelToken, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {
+            auto result = co_await folly::coro::co_awaitTry(
+                std::forward<SemiAwaitables>(awaitables));
+            cancelSource.requestCancellation();
+            std::get<Indices>(results) = std::move(result);
+          }))...);
 
   co_return results;
 }
@@ -407,7 +413,7 @@
   return detail::collectAllImpl(
       tag<detail::CollectAllTask<SemiAwaitables...>, SemiAwaitables...>,
       std::make_index_sequence<sizeof...(SemiAwaitables)>{},
-      mustAwaitImmediatelyUnsafeMover(
+      folly::ext::must_use_immediately_unsafe_mover(
           static_cast<SemiAwaitables&&>(awaitables))...);
 }
 
@@ -417,7 +423,7 @@
   return detail::collectAllTryImpl(
       tag<detail::CollectAllTryTask<SemiAwaitables...>, SemiAwaitables...>,
       std::make_index_sequence<sizeof...(SemiAwaitables)>{},
-      mustAwaitImmediatelyUnsafeMover(
+      folly::ext::must_use_immediately_unsafe_mover(
           static_cast<SemiAwaitables&&>(awaitables))...);
 }
 
@@ -432,7 +438,7 @@
         detail::range_reference_t<InputRange>>>> {
   const folly::Executor::KeepAlive<> executor = co_await co_current_executor;
   const CancellationSource cancelSource;
-  const CancellationToken cancelToken = CancellationToken::merge(
+  const CancellationToken cancelToken = cancellation_token_merge(
       co_await co_current_cancellation_token, cancelSource.getToken());
 
   std::vector<detail::collect_all_try_range_component_t<
@@ -447,9 +453,10 @@
     assert(index < tryResults.size());
 
     try {
-      tryResults[index].emplace(co_await co_viaIfAsync(
-          executor.get_alias(),
-          co_withCancellation(cancelToken, std::move(semiAwaitable))));
+      tryResults[index].emplace(
+          co_await co_viaIfAsync(
+              executor.get_alias(),
+              co_withCancellation(cancelToken, std::move(semiAwaitable))));
     } catch (...) {
       if (!cancelSource.requestCancellation()) {
         firstException = exception_wrapper{current_exception()};
@@ -504,7 +511,7 @@
 auto collectAllRange(InputRange awaitables) -> folly::coro::Task<void> {
   const folly::Executor::KeepAlive<> executor = co_await co_current_executor;
   const CancellationSource cancelSource;
-  const CancellationToken cancelToken = CancellationToken::merge(
+  const CancellationToken cancelToken = cancellation_token_merge(
       co_await co_current_cancellation_token, cancelSource.getToken());
 
   exception_wrapper firstException;
@@ -575,9 +582,10 @@
             co_withCancellation(cancelToken, std::move(semiAwaitable)));
         result.emplace();
       } else {
-        result.emplace(co_await co_viaIfAsync(
-            executor.get_alias(),
-            co_withCancellation(cancelToken, std::move(semiAwaitable))));
+        result.emplace(
+            co_await co_viaIfAsync(
+                executor.get_alias(),
+                co_withCancellation(cancelToken, std::move(semiAwaitable))));
       }
     } catch (...) {
       result.emplaceException(current_exception());
@@ -624,7 +632,7 @@
 
   const folly::Executor::KeepAlive<> executor = co_await co_current_executor;
   const CancellationSource cancelSource;
-  const CancellationToken cancelToken = CancellationToken::merge(
+  const CancellationToken cancelToken = cancellation_token_merge(
       co_await co_current_cancellation_token, cancelSource.getToken());
 
   exception_wrapper firstException;
@@ -741,7 +749,7 @@
       co_await co_current_cancellation_token;
   const CancellationSource cancelSource;
   const CancellationToken cancelToken =
-      CancellationToken::merge(parentCancelToken, cancelSource.getToken());
+      cancellation_token_merge(parentCancelToken, cancelSource.getToken());
 
   exception_wrapper firstException;
 
@@ -764,6 +772,13 @@
       detail::range_reference_t<InputRange>>>
       tryResults;
 
+  if constexpr (is_invocable_v<folly::access::size_fn, InputRange&>) {
+    tryResults.reserve(
+        static_cast<std::size_t>(folly::access::size(awaitables)));
+  } else if constexpr (range_has_known_distance_v<InputRange&>) {
+    tryResults.reserve(static_cast<std::size_t>(std::distance(iter, iterEnd)));
+  }
+
   exception_wrapper iterationException;
 
   auto makeWorker = [&]() -> detail::BarrierTask {
@@ -793,10 +808,11 @@
           tryResult;
 
       try {
-        tryResult.emplace(co_await co_viaIfAsync(
-            executor.get_alias(),
-            co_withCancellation(
-                cancelToken, static_cast<awaitable_t&&>(*awaitable))));
+        tryResult.emplace(
+            co_await co_viaIfAsync(
+                executor.get_alias(),
+                co_withCancellation(
+                    cancelToken, static_cast<awaitable_t&&>(*awaitable))));
       } catch (...) {
         trySetFirstException(exception_wrapper{current_exception()});
       }
@@ -930,9 +946,10 @@
               co_withCancellation(cancelToken, std::move(*awaitable)));
           result.emplace();
         } else {
-          result.emplace(co_await co_viaIfAsync(
-              executor.get_alias(),
-              co_withCancellation(cancelToken, std::move(*awaitable))));
+          result.emplace(
+              co_await co_viaIfAsync(
+                  executor.get_alias(),
+                  co_withCancellation(cancelToken, std::move(*awaitable))));
         }
       } catch (...) {
         result.emplaceException(current_exception());
@@ -1069,16 +1086,15 @@
 }
 
 template <typename InputRange>
-auto collectAnyRange(InputRange awaitables)
-    -> folly::coro::Task<std::pair<
-        size_t,
-        folly::Try<detail::collect_all_range_component_t<
-            detail::range_reference_t<InputRange>>>>> {
+auto collectAnyRange(InputRange awaitables) -> folly::coro::Task<std::pair<
+    size_t,
+    folly::Try<detail::collect_all_range_component_t<
+        detail::range_reference_t<InputRange>>>>> {
   const CancellationToken& parentCancelToken =
       co_await co_current_cancellation_token;
   const CancellationSource cancelSource;
   const CancellationToken cancelToken =
-      CancellationToken::merge(parentCancelToken, cancelSource.getToken());
+      cancellation_token_merge(parentCancelToken, cancelSource.getToken());
 
   std::pair<
       size_t,
@@ -1116,7 +1132,7 @@
       co_await co_current_cancellation_token;
   const CancellationSource cancelSource;
   const CancellationToken cancelToken =
-      CancellationToken::merge(parentCancelToken, cancelSource.getToken());
+      cancellation_token_merge(parentCancelToken, cancelSource.getToken());
 
   size_t nAwaitables;
   std::atomic<std::size_t> nAwaited = 1;
@@ -1155,7 +1171,7 @@
       co_await co_current_cancellation_token;
   const CancellationSource cancelSource;
   const CancellationToken cancelToken =
-      CancellationToken::merge(parentCancelToken, cancelSource.getToken());
+      cancellation_token_merge(parentCancelToken, cancelSource.getToken());
 
   std::vector<detail::collect_all_try_range_component_t<
       detail::range_reference_t<InputRange>>>
diff --git a/folly/folly/coro/Collect.h b/folly/folly/coro/Collect.h
--- a/folly/folly/coro/Collect.h
+++ b/folly/folly/coro/Collect.h
@@ -27,9 +27,9 @@
 #include <folly/coro/ViaIfAsync.h>
 #include <folly/coro/detail/PickTaskWrapper.h>
 #include <folly/coro/detail/Traits.h>
-// `collectAll(coroFutureInt())` makes a `SafeTask`
+// `collectAll(coroFutureInt())` makes a `safe_task`
 #include <folly/coro/safe/SafeTask.h>
-// `collectAll(memberTask())` makes a `NowTask`
+// `collectAll(memberTask())` makes a `now_task`
 #include <folly/coro/safe/NowTask.h>
 
 #include <functional>
@@ -85,20 +85,22 @@
   Container& container_;
 };
 
-// Future: Apply `AsNoexcept` to the task if the entire collection process is
-// noexcept-awaitable.  This would require reworking the implementation a bit,
-// since e.g. `CancellationToken::merge` can throw `bad_alloc`.
+// Future: Apply `value_or_fatal` to the task if the entire collection process
+// is value-only awaitable.  This would require reworking the implementation a
+// bit, since e.g. `cancellation_token_merge` can throw `bad_alloc`.
 template <typename... SemiAwaitables>
-using CollectAllTask = PickTaskWrapper<
+using CollectAllTask = pick_task_wrapper<
     std::tuple<collect_all_component_t<remove_cvref_t<SemiAwaitables>>...>,
-    std::min({safe_alias::maybe_value, safe_alias_of_v<SemiAwaitables>...}),
-    (must_await_immediately_v<SemiAwaitables> || ...)>;
+    std::min(
+        {safe_alias::maybe_value, lenient_safe_alias_of_v<SemiAwaitables>...}),
+    (folly::ext::must_use_immediately_v<SemiAwaitables> || ...)>;
 
 template <typename... SemiAwaitables>
-using CollectAllTryTask = PickTaskWrapper<
+using CollectAllTryTask = pick_task_wrapper<
     std::tuple<collect_all_try_component_t<remove_cvref_t<SemiAwaitables>>...>,
-    std::min({safe_alias::maybe_value, safe_alias_of_v<SemiAwaitables>...}),
-    (must_await_immediately_v<SemiAwaitables> || ...)>;
+    std::min(
+        {safe_alias::maybe_value, lenient_safe_alias_of_v<SemiAwaitables>...}),
+    (folly::ext::must_use_immediately_v<SemiAwaitables> || ...)>;
 
 } // namespace detail
 
@@ -142,7 +144,7 @@
 //       co_await folly::coro::collectAll(doSomething(), doSomethingElse());
 //
 template <typename... SemiAwaitables>
-// Do NOT take awaitables by-reference, that would break `NowTask` safety.
+// Do NOT take awaitables by-reference, that would break `now_task` safety.
 auto collectAll(SemiAwaitables... awaitables)
     -> detail::CollectAllTask<SemiAwaitables...>;
 
@@ -559,11 +561,10 @@
 //       ranges::views::move);
 //
 template <typename InputRange>
-auto collectAnyRange(InputRange awaitables)
-    -> folly::coro::Task<std::pair<
-        size_t,
-        folly::Try<detail::collect_all_range_component_t<
-            detail::range_reference_t<InputRange>>>>>;
+auto collectAnyRange(InputRange awaitables) -> folly::coro::Task<std::pair<
+    size_t,
+    folly::Try<detail::collect_all_range_component_t<
+        detail::range_reference_t<InputRange>>>>>;
 
 ///////////////////////////////////////////////////////////////////////////
 // collectAnyWithoutExceptionRange(RangeOf<SemiAwaitable<T>>&&)
diff --git a/folly/folly/coro/Coroutine.h b/folly/folly/coro/Coroutine.h
--- a/folly/folly/coro/Coroutine.h
+++ b/folly/folly/coro/Coroutine.h
@@ -16,11 +16,9 @@
 
 #pragma once
 
+#include <optional>
 #include <type_traits>
-
-#if __has_include(<variant>)
 #include <variant>
-#endif
 
 #include <folly/Portability.h>
 #include <folly/Utility.h>
@@ -295,25 +293,79 @@
   return t ? true : f ? false : coro::go().eager;
 }
 
-class ExtendedCoroutineHandle;
-
-// Extended promise interface folly::coro types are expected to implement
-class ExtendedCoroutinePromise {
- public:
-  // Types may provide a more efficient resumption path when they know they will
-  // be receiving an error result from the awaitee.
-  // If they do, they might also update the active stack frame.
-  virtual std::pair<ExtendedCoroutineHandle, AsyncStackFrame*> getErrorHandle(
-      exception_wrapper&) = 0;
+template <typename>
+class ExtendedCoroutinePromiseCrtp;
 
- protected:
-  ~ExtendedCoroutinePromise() = default;
-};
+namespace detail {
+template <typename, typename, typename>
+class TaskPromiseWrapperBase;
+}
 
 // Extended version of coroutine_handle<void>
 // Assumes (and enforces) assumption that coroutine_handle is a pointer
 class ExtendedCoroutineHandle {
+ protected:
+  template <typename>
+  friend class ExtendedCoroutinePromiseCrtp;
+  template <typename, typename, typename>
+  friend class detail::TaskPromiseWrapperBase;
+  // This passkey aims to stop end users from calling `getPromiseBase`, which
+  // is an unsafe implementation detail, and to prevent overload ambiguity.
+  //
+  // It also doubles as the sigil for `use_extended_handle_concept`, another
+  // private detail.
+  class PrivateTag {
+   private:
+    friend ExtendedCoroutineHandle;
+    PrivateTag() = default;
+  };
+
+ private:
+  // SFINAE detection for the `use_extended_handle_concept` member type alias
+  // that classes implementing `getErrorHandle` must expose.  We don't want to
+  // use any kind of common base on `TaskWrapperPromise`, be it non-empty
+  // `PromiseBase`, or a dedicated empty tag, since either one would break
+  // empty-base optimization.
+
+  template <typename T>
+  using use_extended_handle_of_ = typename T::use_extended_handle_concept;
+
+  template <typename T, typename Void = void>
+  struct use_extended_handle {
+    static_assert(
+        require_sizeof<T>, "`use_extended_handle` on incomplete type");
+    static constexpr bool value = false;
+  };
+
+  template <typename T>
+  struct use_extended_handle<T, void_t<use_extended_handle_of_<T>>> {
+    static constexpr bool value =
+        std::is_same_v<use_extended_handle_of_<T>, PrivateTag>;
+  };
+
  public:
+  using ErrorHandle = std::pair<ExtendedCoroutineHandle, AsyncStackFrame*>;
+
+  class PromiseBase {
+   private:
+    friend class ExtendedCoroutineHandle;
+    template <typename>
+    friend class ExtendedCoroutinePromiseCrtp;
+
+    using Fn = std::optional<ErrorHandle>(PromiseBase*, exception_wrapper& ex);
+
+    explicit PromiseBase(Fn* fn) : getErrorHandlePtr_(fn) {}
+    ~PromiseBase() = default;
+
+    // A manual vtable with 1 function. Benefits over virtual inheritance:
+    //   - `TaskWrapperPromise` can implement `getErrorHandle` without bloating
+    //     itself with with a vtable it does not need.
+    //   - A tiny binary size win.
+    //   - Derived classes like `TaskPromise` don't have to be `final` in order
+    //     for the compiler to treat them as non-polymorphic.
+    Fn* getErrorHandlePtr_;
+  };
+
   template <typename Promise>
   /*implicit*/ ExtendedCoroutineHandle(
       coroutine_handle<Promise> handle) noexcept
@@ -324,11 +376,10 @@
 
   template <
       typename Promise,
-      std::enable_if_t<
-          std::is_base_of_v<ExtendedCoroutinePromise, Promise>,
-          int> = 0>
-  /*implicit*/ ExtendedCoroutineHandle(Promise* p) noexcept
-      : basic_(coroutine_handle<Promise>::from_promise(*p)), extended_(p) {}
+      std::enable_if_t<use_extended_handle<Promise>::value, int> = 0>
+  /*implicit*/ ExtendedCoroutineHandle(Promise* promise) noexcept
+      : basic_(coroutine_handle<Promise>::from_promise(*promise)),
+        extended_(Promise::getPromiseBase(PrivateTag{}, promise)) {}
 
   ExtendedCoroutineHandle() noexcept = default;
 
@@ -338,10 +389,11 @@
 
   coroutine_handle<> getHandle() const noexcept { return basic_; }
 
-  std::pair<ExtendedCoroutineHandle, AsyncStackFrame*> getErrorHandle(
-      exception_wrapper& ex) {
+  ErrorHandle getErrorHandle(exception_wrapper& ex) {
     if (extended_) {
-      return extended_->getErrorHandle(ex);
+      if (auto res = extended_->getErrorHandlePtr_(extended_, ex)) {
+        return *res;
+      }
     }
     return {basic_, nullptr};
   }
@@ -351,15 +403,55 @@
  private:
   template <typename Promise>
   static auto fromBasic(coroutine_handle<Promise> handle) noexcept {
-    if constexpr (std::is_convertible_v<Promise*, ExtendedCoroutinePromise*>) {
-      return static_cast<ExtendedCoroutinePromise*>(&handle.promise());
+    if constexpr (use_extended_handle<Promise>::value) {
+      return Promise::getPromiseBase(PrivateTag{}, &handle.promise());
     } else {
       return nullptr;
     }
   }
 
   coroutine_handle<> basic_;
-  ExtendedCoroutinePromise* extended_{nullptr};
+  PromiseBase* extended_{nullptr};
+};
+
+// folly::coro types are expected to implement this extended promise interface.
+//
+// It allows types to provide a more efficient resumption path when they know
+// they will be receiving an error result from the awaitee.
+//
+// First, publicly inherit from `ExtendedCoroutinePromiseCrtp<YourPromise>`,
+// Second, implement this static method on `YourPromise`:
+//
+//   static std::optional<ExtendedCoroutineHandle::ErrorHandle>
+//   getErrorHandleImpl(YourPromise&, exception_wrapper&);
+//
+// Return `std::nullopt` to avoid changing the resumption path.  Otherwise,
+// return the `ExtendedCoroutineHandle` to resume & the active stack frame.
+//
+// DANGER: `YourPromise& promise` is a promise instance, but it might NOT
+// directly correspond to a coro frame.  For example, if your coro is wrapped,
+// that promise is a **member** inside a larger wrapper promise for the coro.
+// Therefore, you must NOT call `coroutine_handle<...>::from_promise(promise)`.
+// In the future, the true handle could be supplied, but none of the current
+// coros required it.
+template <typename Promise>
+class ExtendedCoroutinePromiseCrtp
+    : public ExtendedCoroutineHandle::PromiseBase {
+ public:
+  using use_extended_handle_concept = ExtendedCoroutineHandle::PrivateTag;
+
+  static ExtendedCoroutineHandle::PromiseBase* getPromiseBase(
+      ExtendedCoroutineHandle::PrivateTag, ExtendedCoroutinePromiseCrtp* me) {
+    return me;
+  }
+
+ protected:
+  using PromiseBase = typename ExtendedCoroutineHandle::PromiseBase;
+  ExtendedCoroutinePromiseCrtp()
+      : PromiseBase(+[](PromiseBase* p, exception_wrapper& ex) {
+          return Promise::getErrorHandleImpl(*static_cast<Promise*>(p), ex);
+        }) {}
+  ~ExtendedCoroutinePromiseCrtp() = default;
 };
 
 } // namespace folly::coro
diff --git a/folly/folly/coro/Error.h b/folly/folly/coro/Error.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/coro/Error.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// KEEP THIS HEADER MINIMAL -- it is included by all `result` coro users.
+
+#include <cassert>
+#include <type_traits>
+
+#include <folly/ExceptionWrapper.h>
+#include <folly/OperationCancelled.h>
+
+namespace folly::coro {
+
+class co_error final {
+ public:
+  template <
+      typename... A,
+      std::enable_if_t<
+          sizeof...(A) && std::is_constructible<exception_wrapper, A...>::value,
+          int> = 0>
+  explicit co_error(A&&... a) noexcept(
+      std::is_nothrow_constructible<exception_wrapper, A...>::value)
+      : ex_(static_cast<A&&>(a)...) {
+    static_assert(
+        sizeof...(A) != 1 ||
+            !(std::is_same_v<std::decay_t<A>, OperationCancelled> && ...),
+        "Use co_stopped_may_throw instead of co_error(OperationCancelled{})");
+    assert(ex_);
+  }
+
+  const exception_wrapper& exception() const { return ex_; }
+
+  exception_wrapper& exception() { return ex_; }
+
+ private:
+  exception_wrapper ex_;
+};
+
+class co_stopped_may_throw_t final {
+ public:
+  /* implicit */ operator co_error() const {
+    return co_error(make_exception_wrapper<OperationCancelled>());
+  }
+};
+
+inline constexpr co_stopped_may_throw_t co_stopped_may_throw{};
+
+[[deprecated("Use co_stopped_may_throw")]]
+inline constexpr co_stopped_may_throw_t co_cancelled{};
+
+} // namespace folly::coro
diff --git a/folly/folly/coro/FutureUtil.h b/folly/folly/coro/FutureUtil.h
--- a/folly/folly/coro/FutureUtil.h
+++ b/folly/folly/coro/FutureUtil.h
@@ -73,7 +73,7 @@
     co_await baton;
   }
   if (cancelled) {
-    co_yield co_cancelled;
+    co_yield co_stopped_may_throw;
   }
   co_yield co_result(std::move(result));
 }
diff --git a/folly/folly/coro/GmockHelpers.h b/folly/folly/coro/GmockHelpers.h
--- a/folly/folly/coro/GmockHelpers.h
+++ b/folly/folly/coro/GmockHelpers.h
@@ -19,6 +19,7 @@
 #include <atomic>
 #include <type_traits>
 
+#include <folly/coro/BlockingWait.h>
 #include <folly/coro/Coroutine.h>
 #include <folly/coro/GtestHelpers.h>
 #include <folly/coro/Result.h>
@@ -109,13 +110,65 @@
 }
 
 namespace detail {
+
+// Matcher implementation that wraps a Task and delegates to an inner matcher
+// (typically ::testing::Throws or ::testing::ThrowsMessage).
+// This allows CoThrows/CoThrowsMessage to reuse GMock's exception matchers.
+template <typename InnerMatcher>
+class CoroExceptionMatcherImpl {
+ public:
+  explicit CoroExceptionMatcherImpl(InnerMatcher inner_matcher)
+      : inner_matcher_(std::move(inner_matcher)),
+        // Convert the inner matcher to a concrete Matcher type so we can
+        // use its DescribeTo/DescribeNegationTo methods.
+        callable_matcher_(inner_matcher_) {}
+
+  void DescribeTo(std::ostream* os) const { callable_matcher_.DescribeTo(os); }
+
+  void DescribeNegationTo(std::ostream* os) const {
+    callable_matcher_.DescribeNegationTo(os);
+  }
+
+  template <typename T>
+  bool MatchAndExplain(T&& x, ::testing::MatchResultListener* listener) const {
+    // Task is move-only. GoogleTest passes values as const T&, but we need
+    // to move the Task into blockingWait. We use const_cast to enable the
+    // move. This is safe because each matcher instance is used only once.
+    using MutableT = std::remove_const_t<std::remove_reference_t<T>>;
+    auto& mutableX = const_cast<MutableT&>(x);
+
+    // Wrap the task in a shared_ptr so the callable can be const-invoked
+    // (GMock's Throws matcher requires the callable to be const-invocable).
+    auto taskHolder =
+        std::make_shared<std::optional<MutableT>>(std::move(mutableX));
+
+    std::function<void()> callable = [taskHolder]() {
+      if (!taskHolder->has_value()) {
+        throw std::logic_error("Task already consumed");
+      }
+      auto task = std::move(taskHolder->value());
+      taskHolder->reset();
+      blockingWait(std::move(task));
+    };
+
+    // Delegate to the inner matcher (Throws/ThrowsMessage).
+    // GMock's Throws matchers expect a callable, which they invoke to check
+    // for exceptions.
+    return callable_matcher_.MatchAndExplain(callable, listener);
+  }
+
+ private:
+  InnerMatcher inner_matcher_;
+  ::testing::Matcher<std::function<void()>> callable_matcher_;
+};
+
 template <typename Fn>
 auto makeCoAction(Fn&& fn) {
   static_assert(
       std::is_copy_constructible_v<remove_cvref_t<Fn>>,
       "Fn should be copyable to allow calling mocked call multiple times.");
 
-  using Ret = std::invoke_result_t<remove_cvref_t<Fn>&&>;
+  using Ret = invoke_result_t<remove_cvref_t<Fn>&&>;
   return ::testing::InvokeWithoutArgs(
       [fn = std::forward<Fn>(fn)]() mutable -> Ret { return co_invoke(fn); });
 }
@@ -243,6 +296,70 @@
   return detail::makeCoAction([ex = std::forward<Ex>(e)]() -> Task<T> {
     co_yield co_error(ex);
   });
+}
+
+template <typename T>
+auto CoStoppedMayThrow() {
+  return detail::makeCoAction([]() -> Task<T> {
+    co_yield co_stopped_may_throw;
+  });
+}
+
+// CoThrows()
+// CoThrows(exceptionMatcher)
+// CoThrowsMessage(messageMatcher)
+//
+// These matchers accept a folly::coro::Task and verify that when awaited,
+// it throws an exception with the given type and properties.
+//
+// Examples:
+//
+//   EXPECT_THAT(
+//       []() -> folly::coro::Task<void> {
+//         throw std::runtime_error("message");
+//         co_return;
+//       }(),
+//       CoThrows<std::runtime_error>());
+//
+//   EXPECT_THAT(
+//       []() -> folly::coro::Task<void> {
+//         throw std::runtime_error("message");
+//         co_return;
+//       }(),
+//       CoThrowsMessage<std::runtime_error>(HasSubstr("message")));
+//
+//   EXPECT_THAT(
+//       []() -> folly::coro::Task<void> {
+//         throw std::runtime_error("message");
+//         co_return;
+//       }(),
+//       CoThrows<std::runtime_error>(
+//           Property(&std::runtime_error::what, HasSubstr("message"))));
+
+template <typename Err>
+auto CoThrows() {
+  return ::testing::MakePolymorphicMatcher(
+      detail::CoroExceptionMatcherImpl<decltype(::testing::Throws<Err>())>(
+          ::testing::Throws<Err>()));
+}
+
+template <typename Err, typename ExceptionMatcher>
+auto CoThrows(const ExceptionMatcher& exception_matcher) {
+  return ::testing::MakePolymorphicMatcher(
+      detail::CoroExceptionMatcherImpl<decltype(::testing::Throws<Err>(
+          exception_matcher))>(::testing::Throws<Err>(exception_matcher)));
+}
+
+template <typename Err, typename MessageMatcher>
+auto CoThrowsMessage(MessageMatcher&& message_matcher) {
+  static_assert(
+      std::is_base_of_v<std::exception, Err>,
+      "expected an std::exception-derived type");
+  return ::testing::MakePolymorphicMatcher(
+      detail::CoroExceptionMatcherImpl<decltype(::testing::ThrowsMessage<Err>(
+          std::forward<MessageMatcher>(message_matcher)))>(
+          ::testing::ThrowsMessage<Err>(
+              std::forward<MessageMatcher>(message_matcher))));
 }
 
 } // namespace gmock_helpers
diff --git a/folly/folly/coro/GtestHelpers.h b/folly/folly/coro/GtestHelpers.h
--- a/folly/folly/coro/GtestHelpers.h
+++ b/folly/folly/coro/GtestHelpers.h
@@ -141,6 +141,7 @@
       : public test_suite_name {                                               \
    public:                                                                     \
     GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {}                    \
+    ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default;  \
     void TestBody() override;                                                  \
     folly::coro::Task<void> co_TestBody();                                     \
                                                                                \
@@ -162,8 +163,13 @@
     static int gtest_registering_dummy_ [[maybe_unused]];                      \
     GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                         \
     (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete;     \
+    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                         \
+    (GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) = delete;          \
     GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=(            \
         const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) =          \
+        delete; /* NOLINT */                                                   \
+    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=(            \
+        GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) =               \
         delete; /* NOLINT */                                                   \
   };                                                                           \
   int GTEST_TEST_CLASS_NAME_(                                                  \
diff --git a/folly/folly/coro/Noexcept.h b/folly/folly/coro/Noexcept.h
deleted file mode 100644
--- a/folly/folly/coro/Noexcept.h
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/coro/TaskWrapper.h>
-#include <folly/coro/ViaIfAsync.h>
-
-#if FOLLY_HAS_COROUTINES
-
-/// ## When to use this
-///
-/// Use `AsNoexcept<>` only with APIs that only take coroutines that MUST NOT
-/// throw when awaited -- like `co_cleanup()` or async scopes.  If your code
-/// compiles without `AsNoexcept<>`, you do not need it!
-///
-/// ## This is probably not the utility you are looking for!
-///
-///   - This is not related to `co_nothrow`, which is a perf optimization for
-///     coroutines with hot exceptions.  `co_await co_nothrow(foo())` means
-///     "when `foo()` throws, exit the current coro and pass the exception to
-///     whatever is awaiting me".  This saves the ~usec overhead of rethrow.
-///
-///   - If your project likes to avoid exceptions, that is not a great reason
-///     to reflexively make all your coros `AsNoexcept<>`, for these reasons:
-///       * Since `AsNoexcept<>` is implemented as a wrapper, it may reduce
-///         your build speed.
-///       * Coro frame allocation & construction can still throw (unless you
-///         also mark the coro function `noexcept`).
-///       * Emitting the `noexcept` -> `std::terminate` offramp can sometimes
-///         be a pessimization compared to normal exception propagation.
-///
-///   - We do not provide a helper like `co_await co_fatalOnThrow(...)`, since
-///     most callsites should either handle the exception (possibly with
-///     `co_awaitTry`), or let it fly.
-///
-/// ## Why does this even exist, what's wrong with `noexcept`?
-///
-/// Some `folly::coro` libraries require `AsNoexcept<>` is to clearly signal a
-/// **firm contract** between the API and the user-supplied coroutine.  This is
-/// only appropriate in situations similar to sync destructors, where the API
-/// has no good recourse in case of a thrown exception.
-///
-/// We need a special wrapper type because marking a coroutine function
-/// `noexcept` says nothing about whether awaiting the resulting coroutine can
-/// throw.  Rather, it makes fatal any exception thrown during the construction
-/// of the coroutine object itself (i.e.  a `bad_alloc` for the frame, or
-/// errors copying/moving the args).
-///
-/// ## How exactly does `AsNoexcept<>` work?
-///
-/// `Noexcept.h` lets you mark coroutine types as `noexcept_awaitable_v`:
-///
-///   []() -> AsNoexcept<Task<T>, OnCancel(defaultT())> { co_return ...; }
-///
-/// This function creates a coroutine whose awaitable is that of the inner
-/// task, but wrapped with `detail::NoexceptAwaitable<...>`.
-//
-/// The latter is an awaitable-wrapper similar to `co_awaitTry()`, except that
-/// it terminates the program if `someAwaitable()` resumes with a thrown
-/// exception.  So, both of these will never throw, but the former returns a
-/// `Try` while latter returns an unwrapped value:
-///
-///   co_await co_awaitTry(intTask())  // `Try<int>`
-///   co_await detail::NoexceptAwaitable<int, OnCancel(0)>{intTask()} // `int`
-///
-/// Both the coroutine `AsNoexcept<Task<...>, ...>` and the preceding 2
-/// awaitables return `true` for `noexcept_awaitable_v`.
-///
-/// `AsNoexcept<>` / `NoexceptAwaitable<>` compose properly with other coro-
-/// and awaitable-wrappers.  But, not all combinations make sense -- see the
-/// test, and/or extend it if needed.  For example, the outer wrapper is
-/// useless in `NoexceptAwaitable<...>(co_awaitTry(...))`, since exceptions
-/// would already have been routed into a `Try`.
-
-namespace folly::coro {
-
-struct TerminateOnCancel {};
-inline constexpr TerminateOnCancel terminateOnCancel{};
-
-template <typename T>
-struct OnCancel {
-  T privateVal_; // only `public` to make this a structural type
-  T onCancelDefaultValue() const noexcept {
-    static_assert(std::is_nothrow_copy_constructible_v<T>);
-    return privateVal_;
-  }
-  consteval explicit OnCancel(T t) : privateVal_{std::move(t)} {}
-};
-
-template <>
-struct OnCancel<void> {
-  void onCancelDefaultValue() const noexcept {}
-  consteval explicit OnCancel() = default;
-};
-
-namespace detail {
-
-template <typename Awaitable, auto CancelCfg>
-class NoexceptAwaiter {
- private:
-  using Awaiter = awaiter_type_t<Awaitable>;
-  Awaiter awaiter_;
-
- public:
-  explicit NoexceptAwaiter(Awaitable&& awaiter)
-      : awaiter_(get_awaiter(static_cast<Awaitable&&>(awaiter))) {}
-
-  auto await_ready() noexcept -> decltype(awaiter_.await_ready()) {
-    // As of this writing, all `await_ready` in `folly::coro` are `noexcept`.
-    // If this is legitimately triggered, then we can decide the right policy.
-    static_assert(noexcept(awaiter_.await_ready()));
-    return awaiter_.await_ready();
-  }
-
-  // `noexcept` forces any rethrown exceptions to `std::terminate`
-  auto await_resume() noexcept -> decltype(awaiter_.await_resume()) {
-    if constexpr (std::is_same_v<decltype(CancelCfg), TerminateOnCancel>) {
-      return awaiter_.await_resume();
-    } else {
-      try {
-        return awaiter_.await_resume();
-      } catch (const OperationCancelled&) {
-        // IMPORTANT: If you want to extend this protocol to pull out a default
-        // value from the awaiter, be sure to add this assert:
-        // static_assert(noexcept(CancelCfg.onCancelDefaultValue(awaiter_)));
-        return CancelCfg.onCancelDefaultValue();
-      }
-    }
-  }
-
-  // `noexcept` here as well, because the underlying awaitable might
-  // have a throwing `await_suspend`, and those exceptions propagate
-  // to the parent coro promise, bypassing `await_resume`.
-  // Demo: https://godbolt.org/z/Edfj8P8be
-  template <typename Promise>
-  auto await_suspend(coroutine_handle<Promise> coro) noexcept
-      -> decltype(awaiter_.await_suspend(coro)) {
-    return awaiter_.await_suspend(coro);
-  }
-};
-
-template <typename, auto>
-class NoexceptAwaitable;
-
-template <auto CancelCfg>
-struct NoexceptAwaitableWithCancelCfg {
-  template <typename T>
-  using apply = NoexceptAwaitable<T, CancelCfg>;
-};
-
-template <typename T, auto CancelCfg>
-class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] NoexceptAwaitable
-    : public CommutativeWrapperAwaitable<
-          NoexceptAwaitableWithCancelCfg<CancelCfg>::template apply,
-          T> {
- public:
-  using CommutativeWrapperAwaitable<
-      NoexceptAwaitableWithCancelCfg<CancelCfg>::template apply,
-      T>::CommutativeWrapperAwaitable;
-
-  template <typename T2 = T, std::enable_if_t<is_awaitable_v<T2>, int> = 0>
-  NoexceptAwaiter<T, CancelCfg> operator co_await() && {
-    return NoexceptAwaiter<T, CancelCfg>{std::move(this->inner_)};
-  }
-
-  using folly_private_noexcept_awaitable_t = std::true_type;
-};
-
-} // namespace detail
-
-#if FOLLY_HAS_IMMOVABLE_COROUTINES
-
-template <typename Inner, auto CancelCfg>
-class AsNoexcept;
-// NB: While it'd be prettier to have `AsNoexcept` branch on whether the inner
-// task has an executor, a separate template is much simpler.
-template <typename Inner, auto CancelCfg>
-class AsNoexceptWithExecutor;
-
-namespace detail {
-template <typename Inner, auto CancelCfg>
-struct AsNoexceptWithExecutorCfg {
-  using InnerTaskWithExecutorT = Inner;
-  using WrapperTaskT = AsNoexcept<
-      typename Inner::folly_private_task_without_executor_t,
-      CancelCfg>;
-  template <typename Awaitable> // library-internal, meant to be by-rref
-  static inline auto wrapAwaitable(Awaitable&& awaitable) noexcept {
-    // Assert can be removed, I was concerned if we accidentally double-wrap
-    static_assert(!noexcept_awaitable_v<Awaitable>);
-    return detail::NoexceptAwaitable<Awaitable, CancelCfg>{
-        mustAwaitImmediatelyUnsafeMover(static_cast<Awaitable&&>(awaitable))()};
-  }
-};
-template <typename Inner, auto CancelCfg>
-using AsNoexceptWithExecutorBase = TaskWithExecutorWrapperCrtp<
-    AsNoexceptWithExecutor<Inner, CancelCfg>,
-    AsNoexceptWithExecutorCfg<Inner, CancelCfg>>;
-} // namespace detail
-
-template <typename Inner, auto CancelCfg = OnCancel<void>{}>
-class FOLLY_NODISCARD AsNoexceptWithExecutor final
-    : public detail::AsNoexceptWithExecutorBase<Inner, CancelCfg> {
- protected:
-  using detail::AsNoexceptWithExecutorBase<Inner, CancelCfg>::
-      AsNoexceptWithExecutorBase;
-
- public:
-  using folly_private_noexcept_awaitable_t = std::true_type;
-};
-
-namespace detail {
-
-template <typename... BaseArgs>
-class AsNoexceptTaskPromiseWrapper final
-    : public TaskPromiseWrapper<BaseArgs...> {};
-
-template <typename Inner, auto CancelCfg>
-struct AsNoexceptCfg {
-  using ValueT = semi_await_result_t<Inner>;
-  using InnerTaskT = Inner;
-  using TaskWithExecutorT = AsNoexceptWithExecutor<
-      decltype(co_withExecutor(
-          FOLLY_DECLVAL(Executor::KeepAlive<>), FOLLY_DECLVAL(Inner))),
-      CancelCfg>;
-  using PromiseT = AsNoexceptTaskPromiseWrapper<
-      ValueT,
-      AsNoexcept<Inner, CancelCfg>,
-      typename folly::coro::coroutine_traits<Inner>::promise_type>;
-  template <typename Awaitable> // library-internal, meant to be by-rref
-  static inline auto wrapAwaitable(Awaitable&& awaitable) noexcept {
-    // Assert can be removed, I was concerned if we accidentally double-wrap
-    static_assert(!noexcept_awaitable_v<Awaitable>);
-    return detail::NoexceptAwaitable<Awaitable, CancelCfg>{
-        static_cast<Awaitable&&>(awaitable)};
-  }
-};
-
-template <typename Inner, auto CancelCfg>
-using AsNoexceptBase = TaskWrapperCrtp<
-    AsNoexcept<Inner, CancelCfg>,
-    AsNoexceptCfg<Inner, CancelCfg>>;
-
-// CAUTION: `as_noexcept_rewrapper` gives you the power to wrap and unwrap
-// `AsNoexcept`, so you must be extremely careful to preserve behavior:
-//   - The unwrapped task must be rewrapped before awaiting.
-//   - You must not wrap any other task.
-
-template <typename>
-struct as_noexcept_rewrapper {
-  static inline constexpr bool as_noexcept_wrapped = false;
-  static auto wrap_with(auto fn) { return fn(); }
-};
-
-template <typename Inner, auto Cfg>
-struct as_noexcept_rewrapper<AsNoexcept<Inner, Cfg>> {
-  static inline constexpr bool as_noexcept_wrapped = true;
-  static Inner unwrapTask(AsNoexcept<Inner, Cfg>&& t) {
-    return std::move(t).unwrapTask();
-  }
-  static auto wrap_with(auto fn) {
-    return AsNoexcept<decltype(fn()), Cfg>{fn()};
-  }
-};
-
-} // namespace detail
-
-template <typename Inner, auto CancelCfg = OnCancel<void>{}>
-class FOLLY_CORO_TASK_ATTRS AsNoexcept final
-    : public detail::AsNoexceptBase<Inner, CancelCfg> {
- protected:
-  using detail::AsNoexceptBase<Inner, CancelCfg>::AsNoexceptBase;
-
-  template <typename> // Can unwrap and re-wrap (construct)
-  friend struct detail::as_noexcept_rewrapper;
-
- public:
-  using folly_private_noexcept_awaitable_t = std::true_type;
-};
-
-#endif // FOLLY_HAS_IMMOVABLE_COROUTINES
-
-} // namespace folly::coro
-
-#endif // FOLLY_HAS_COROUTINES
diff --git a/folly/folly/coro/Nothrow.h b/folly/folly/coro/Nothrow.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/coro/Nothrow.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/ExceptionWrapper.h>
+#include <folly/Portability.h> // FOLLY_HAS_COROUTINES
+#include <folly/coro/ViaIfAsync.h>
+
+// For DCHECK, `ViaIfAsync.h` depends on it already.
+#include <glog/logging.h>
+
+#if FOLLY_HAS_COROUTINES
+
+namespace folly::coro {
+
+namespace detail {
+
+template <typename T>
+class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] NothrowAwaitable;
+
+// The `!value_only_awaitable_v` constraint stops `co_nothrow()` from wrapping
+// `value_or_error_or_stopped`, `co_awaitTry`, `AsNoexcept`, etc.
+//
+// Rationale: Instead, we could do:
+//   - (not very useful) Nothing -- modeling the behavior that the exception
+//     was already captured by the inner type.
+//   - (highly unexpected / buggy) Prevent the inner type from capturing the
+//     exception, and force its propagation up-stack.
+// This was banned because real-world users didn't uniformly expect one of the
+// behaviors over the other, and nobody really **needs** this to work.
+template <typename T>
+class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]
+NothrowAwaitable : public CommutativeWrapperAwaitable<NothrowAwaitable, T> {
+ public:
+  using CommutativeWrapperAwaitable<NothrowAwaitable, T>::
+      CommutativeWrapperAwaitable;
+
+  template <
+      typename T2 = T,
+      std::enable_if_t<!value_only_awaitable_v<T>, int> = 0>
+  T2&& unwrap() {
+    return std::move(this->inner_);
+  }
+};
+
+template <typename>
+class ValueOrError;
+template <typename>
+class ValueOrErrorImpl;
+
+// Mixin supporting `co_nothrow()` and `value_or_error()` for tasks & generators
+class BypassExceptionThrowing {
+ private:
+  enum class BypassMode : uint8_t {
+    // Default state for `co_await task()`.  The corresponding
+    // `await_transform` calls must call `maybeActivate()`, which will either
+    // promote `REQUESTED` to `ACTIVE`, or reset any `ACTIVE` state from a
+    // prior suspension point back to `INACTIVE`.
+    INACTIVE,
+    // State after `await_transform` with a `co_nothrow` awaitable.
+    ACTIVE,
+    // State before `await_transform` with a `co_nothrow` awaitable.
+    REQUESTED,
+    // State for `value_or_error()` only allowing for nothrow-propagation of
+    // `OperationCancelled` to the parent.
+    ONLY_WHEN_OPERATION_CANCELLED,
+  } bypassMode_{BypassMode::INACTIVE};
+
+ protected:
+  // This write interface is protected to alert future authors -- this is a
+  // TIGHTLY COUPLED DETAIL.  See the analogous note in `BasePromise`.
+  template <typename>
+  friend class BasePromise;
+
+  template <typename Awaitable>
+  void maybeActivate() {
+    if (is_instantiation_of_v<ValueOrErrorImpl, Awaitable>) {
+      DCHECK(
+          // normal `value_or_error`: bypass `OperationCancelled` handling.
+          bypassMode_ == BypassMode::ONLY_WHEN_OPERATION_CANCELLED ||
+          // inner awaitable is value-only, no bypass needed.
+          bypassMode_ == BypassMode::INACTIVE);
+    } else {
+      // Awaitable should've been unwrapped before getting here.
+      static_assert(
+          !is_instantiation_of_v<NothrowAwaitable, Awaitable> &&
+          !is_instantiation_of_v<ValueOrError, Awaitable>);
+      bypassMode_ = bypassMode_ == BypassMode::REQUESTED
+          ? BypassMode::ACTIVE
+          : BypassMode::INACTIVE;
+    }
+  }
+
+  // Implements `co_nothrow` -- this gets called only from the matching
+  // `await_transform(NothrowAwaitable<Awaitable>)`.  The subsequent
+  // `await_transform(Awaitable)` maps `REQUESTED` to `ACTIVE.
+  template <typename Awaitable>
+  void requestDueToNothrow() {
+    // `co_nothrow` is incompatible with noexcept-awaitables, doc above.
+    static_assert(!value_only_awaitable_v<Awaitable>);
+    bypassMode_ = BypassMode::REQUESTED;
+  }
+
+  template <typename Awaitable>
+  void requestDueToValueOrError() {
+    // If the inner awaitable is value-only (e.g., `value_or_fatal`), it
+    // already guarantees no exceptions escape.  Do NOT activate bypass mode,
+    // as that would intercept exceptions before the inner awaitable's
+    // `await_resume_result()` can apply its policy.
+    if constexpr (!value_only_awaitable_v<Awaitable>) {
+      bypassMode_ = BypassMode::ONLY_WHEN_OPERATION_CANCELLED;
+    }
+  }
+
+ public: // Otherwise we'd also need to friend `AsyncGenerator`, etc
+  bool shouldBypassFor(exception_wrapper& ex) {
+    return (bypassMode_ == BypassMode::ACTIVE) ||
+        (bypassMode_ == BypassMode::ONLY_WHEN_OPERATION_CANCELLED &&
+         // Today, this incurs RTTI cost, but once we migrate coros to use
+         // `rich_exception_ptr`, this will often be RTTI-free.
+         ex.get_exception<folly::OperationCancelled>());
+  }
+};
+
+} // namespace detail
+
+template <
+    typename Awaitable,
+    std::enable_if_t<
+        !value_only_awaitable_v<Awaitable> && // Comment on `NothrowAwaitable`
+            !folly::ext::must_use_immediately_v<Awaitable>,
+        int> = 0>
+detail::NothrowAwaitable<remove_cvref_t<Awaitable>> co_nothrow(
+    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable&& awaitable) {
+  return detail::NothrowAwaitable<remove_cvref_t<Awaitable>>{
+      static_cast<Awaitable&&>(awaitable)};
+}
+template <
+    typename Awaitable,
+    std::enable_if_t<
+        !value_only_awaitable_v<Awaitable> && // Comment on `NothrowAwaitable`
+            folly::ext::must_use_immediately_v<Awaitable>,
+        int> = 0>
+detail::NothrowAwaitable<remove_cvref_t<Awaitable>> co_nothrow(
+    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {
+  return detail::NothrowAwaitable<remove_cvref_t<Awaitable>>{
+      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))()};
+}
+
+} // namespace folly::coro
+
+#endif // FOLLY_HAS_COROUTINES
diff --git a/folly/folly/coro/Promise.h b/folly/folly/coro/Promise.h
--- a/folly/folly/coro/Promise.h
+++ b/folly/folly/coro/Promise.h
@@ -222,8 +222,24 @@
    */
   Future() = default;
 
-  Future(Future&&) noexcept = default;
-  Future& operator=(Future&&) noexcept = default;
+  Future(Future&& other) noexcept
+      : cs_(std::move(other.cs_)),
+        state_(std::exchange(other.state_, nullptr)),
+        ct_(std::move(other.ct_)),
+        hasCancelTokenOverride_(
+            std::exchange(other.hasCancelTokenOverride_, false)) {}
+
+  Future& operator=(Future&& other) noexcept {
+    if (this != &other) {
+      cs_ = std::move(other.cs_);
+      state_ = std::exchange(other.state_, nullptr);
+      ct_ = std::move(other.ct_);
+      hasCancelTokenOverride_ =
+          std::exchange(other.hasCancelTokenOverride_, false);
+    }
+    return *this;
+  }
+
   Future(const Future&) = delete;
   Future& operator=(const Future&) = delete;
 
@@ -258,6 +274,10 @@
     return WaitOperation{*this};
   }
 
+  bool valid() const noexcept { return state_ != nullptr; }
+
+  explicit operator bool() const noexcept { return valid(); }
+
   bool isReady() const noexcept { return state_->ready.ready(); }
 
   friend Future co_withCancellation(
@@ -268,7 +288,8 @@
     return std::move(future);
   }
 
-  using folly_private_safe_alias_t = safe_alias_of<T>;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t = safe_alias_of<T, Default>;
 
  private:
   Future(CancellationSource cs, detail::PromiseState<T>& state)
diff --git a/folly/folly/coro/Ready.h b/folly/folly/coro/Ready.h
deleted file mode 100644
--- a/folly/folly/coro/Ready.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/Executor.h>
-#include <folly/coro/Result.h>
-#include <folly/coro/WithAsyncStack.h>
-
-/// Use `co_ready` to "await" synchronous coroutine types from inside async
-/// coroutines like `coro::Task`. For example:
-///
-///   result<int> getN();
-///   int n = co_await co_ready(getN());
-///
-/// Also see `co_await_result` (`AwaitResult.h`) and `co_result` (`Result.h`).
-///
-/// If you need to optimize away ALL exception throwing in **async** code,
-/// `co_ready` is not your top choice.  In a `Task` coro:
-///
-///   auto v = co_await co_nothrow(asyncMayError()); // best practice
-///   auto v = co_await co_ready(co_await_result(asyncMayError())); // too long
-///
-/// However, when you are calling synchronous `result` functions, or need to
-/// efficiently handle **some** async errors, `co_ready` is your friend:
-///
-///   auto res = syncResultFn(); // or `co_await co_await_result(asyncFn())`
-///   if (auto* ex = get_exception<MyError>(res)) {
-///     /* handle ex */
-///   } else {
-///     auto v = co_await co_ready(std::move(res)); // propagate unhandled
-///   }
-///
-/// This pattern has a few good properties:
-///   - Easy error handling -- extracts the value from its argument, or
-///     short-circuit any error to current coro's awaiter.
-///   - Unlike `catch (const std::exception& ex)`, won't catch (and therefore
-///     break) cancellation.
-///   - The error path is MUCH more efficient (3-30 nanoseconds) than
-///     `value_or_throw()` (1 microsecond).
-///
-/// We don't support `co_await syncResultFn()` to avoids confusion about which
-/// parts of the code are sync vs async.  The distinction is critical, since
-/// one must not hold non-coro mutexes across async suspend points.
-///
-/// Future:
-///   - Adding `std::ref` / `std::cref`, and possibly `folly::rref` variants of
-///     this (as in `result.h`) might improve performance in hot code.
-///   - The current implementation is `result`-only.  If you have a need, it
-///     would be fine to add the analogous specialization for `Try`.  Just be
-///     mindful of its two warts: empty state and empty `exception_wrapper`.
-
-namespace folly::coro {
-
-template <typename>
-class co_ready;
-
-#if FOLLY_HAS_RESULT
-
-template <typename T>
-class co_ready<result<T>> {
- private:
-  result<T> res_;
-
- public:
-  explicit co_ready(result<T>&& res) : res_(std::move(res)) {}
-
-  bool await_ready() const noexcept { return res_.has_value(); }
-
-  auto await_resume() noexcept -> decltype(std::move(res_).value_or_throw()) {
-    return std::move(res_).value_or_throw();
-  }
-
-  template <typename Promise>
-  auto await_suspend(
-      std::coroutine_handle<Promise> awaitingCoroutine) noexcept {
-    auto& promise = awaitingCoroutine.promise();
-    // We have to use the legacy API because (1) `folly::coro` internals still
-    // model cancellation as an exception, (2) to use `co_cancelled` here we'd
-    // have to check `res_` for `OperationCancelled` which can cost 50-100ns+.
-    auto awaiter = promise.yield_value(co_error(
-        std::move(res_).non_value().get_legacy_error_or_cancellation()));
-    return awaiter.await_suspend(awaitingCoroutine);
-  }
-
-  friend auto co_viaIfAsync(
-      const Executor::KeepAlive<>&, co_ready&& r) noexcept {
-    return std::move(r);
-  }
-
-  friend auto tag_invoke(cpo_t<co_withAsyncStack>, co_ready&& r) noexcept {
-    return std::move(r);
-  }
-};
-
-template <typename T>
-co_ready(result<T>&&) -> co_ready<result<T>>;
-
-#endif // FOLLY_HAS_RESULT
-
-} // namespace folly::coro
diff --git a/folly/folly/coro/Result.h b/folly/folly/coro/Result.h
--- a/folly/folly/coro/Result.h
+++ b/folly/folly/coro/Result.h
@@ -19,34 +19,12 @@
 #include <cassert>
 #include <type_traits>
 
-#include <folly/ExceptionWrapper.h>
-#include <folly/OperationCancelled.h>
 #include <folly/Try.h>
-#include <folly/result/result.h>
-
-namespace folly {
-namespace coro {
-
-class co_error final {
- public:
-  template <
-      typename... A,
-      std::enable_if_t<
-          sizeof...(A) && std::is_constructible<exception_wrapper, A...>::value,
-          int> = 0>
-  explicit co_error(A&&... a) noexcept(
-      std::is_nothrow_constructible<exception_wrapper, A...>::value)
-      : ex_(static_cast<A&&>(a)...) {
-    assert(ex_);
-  }
-
-  const exception_wrapper& exception() const { return ex_; }
-
-  exception_wrapper& exception() { return ex_; }
+#include <folly/coro/Error.h> // compat: used to be the same header
+#include <folly/result/try.h>
+#include <folly/result/value_only_result.h>
 
- private:
-  exception_wrapper ex_;
-};
+namespace folly::coro {
 
 template <typename T>
 class co_result final {
@@ -58,12 +36,18 @@
   }
 
 #if FOLLY_HAS_RESULT
-  // Covered in `AwaitResultTest.cpp`, unlike the rest of this file, which is
+  // Covered in `ValueOrErrorTest.cpp`, unlike the rest of this file, which is
   // covered in `TaskTest.cpp`.
   template <std::same_as<folly::result<T>> U> // no implicit ctors for `result`
   explicit co_result(U result) noexcept(
       std::is_nothrow_move_constructible<T>::value)
       : co_result(result_to_try(std::move(result))) {}
+
+  // value_only_result always has a value, so conversion to Try is simple.
+  template <std::same_as<folly::value_only_result<T>> U>
+  explicit co_result(U valueOnlyResult) noexcept(
+      std::is_nothrow_move_constructible<T>::value)
+      : result_(std::move(valueOnlyResult).value_or_throw()) {}
 #endif
 
   const Try<T>& result() const { return result_; }
@@ -77,16 +61,8 @@
 #if FOLLY_HAS_RESULT
 template <typename T>
 co_result(result<T>) -> co_result<T>;
+template <typename T>
+co_result(value_only_result<T>) -> co_result<T>;
 #endif
 
-class co_cancelled_t final {
- public:
-  /* implicit */ operator co_error() const {
-    return co_error(OperationCancelled{});
-  }
-};
-
-inline constexpr co_cancelled_t co_cancelled{};
-
-} // namespace coro
-} // namespace folly
+} // namespace folly::coro
diff --git a/folly/folly/coro/Retry.h b/folly/folly/coro/Retry.h
--- a/folly/folly/coro/Retry.h
+++ b/folly/folly/coro/Retry.h
@@ -128,7 +128,7 @@
 
     const auto& cancelToken = co_await co_current_cancellation_token;
     if (cancelToken.isCancellationRequested()) {
-      co_yield folly::coro::co_error(OperationCancelled{});
+      co_yield folly::coro::co_stopped_may_throw;
     }
 
     --retriesRemaining_;
@@ -200,8 +200,8 @@
         ? std::exp(dist{0., relativeJitterStdDev_}(randomGen_))
         : 1.;
     // TODO T186551522 Calculate backoff in microseconds.
-    const auto backoffNominal =
-        Duration(folly::constexpr_clamp_cast<Duration::rep>(
+    const auto backoffNominal = Duration(
+        folly::constexpr_clamp_cast<Duration::rep>(
             jitter * minBackoff_.count() * std::pow(2, retryCount_ - 1u)));
 
     const Duration backoff = std::clamp(
@@ -212,7 +212,7 @@
     /// Check to see if we were cancelled during the sleep.
     const auto& cancelToken = co_await co_current_cancellation_token;
     if (cancelToken.isCancellationRequested()) {
-      co_yield folly::coro::co_cancelled;
+      co_yield folly::coro::co_stopped_may_throw;
     }
   }
 
diff --git a/folly/folly/coro/RustAdaptors.h b/folly/folly/coro/RustAdaptors.h
--- a/folly/folly/coro/RustAdaptors.h
+++ b/folly/folly/coro/RustAdaptors.h
@@ -71,6 +71,11 @@
     }
   }
 
+  PollFuture(const PollFuture&) = delete;
+  PollFuture& operator=(const PollFuture&) = delete;
+  PollFuture(PollFuture&&) = delete;
+  PollFuture& operator=(PollFuture&&) = delete;
+
   Poll poll(Waker waker) {
     while (true) {
       std::queue<Func> funcs;
diff --git a/folly/folly/coro/ScopeExit.h b/folly/folly/coro/ScopeExit.h
--- a/folly/folly/coro/ScopeExit.h
+++ b/folly/folly/coro/ScopeExit.h
@@ -141,8 +141,9 @@
 
   template <typename Awaitable>
   auto await_transform(Awaitable&& awaitable) {
-    return folly::coro::co_withAsyncStack(folly::coro::co_viaIfAsync(
-        executor_.get_alias(), static_cast<Awaitable&&>(awaitable)));
+    return folly::coro::co_withAsyncStack(
+        folly::coro::co_viaIfAsync(
+            executor_.get_alias(), static_cast<Awaitable&&>(awaitable)));
   }
 
   folly::AsyncStackFrame& getAsyncFrame() noexcept {
diff --git a/folly/folly/coro/SharedLock.h b/folly/folly/coro/SharedLock.h
--- a/folly/folly/coro/SharedLock.h
+++ b/folly/folly/coro/SharedLock.h
@@ -36,7 +36,7 @@
 namespace detail {
 
 template <typename Mutex, typename Policy>
-class FOLLY_NODISCARD LockBase {
+class [[nodiscard]] LockBase {
  public:
   static_assert(std::is_same_v<
                 bool,
diff --git a/folly/folly/coro/SharedMutex.h b/folly/folly/coro/SharedMutex.h
--- a/folly/folly/coro/SharedMutex.h
+++ b/folly/folly/coro/SharedMutex.h
@@ -551,7 +551,13 @@
   static constexpr std::size_t kUpgradeLockFlag = 2;
   static constexpr std::size_t kSharedLockCountIncrement = 4;
 
-  folly::Synchronized<State, folly::SpinLock> state_;
+  struct ExclusiveMutex {
+    std::unique_ptr<std::mutex> impl_{std::make_unique<std::mutex>()};
+    void lock() { return impl_->lock(); }
+    void unlock() { return impl_->unlock(); }
+  };
+  using Mutex = std::conditional_t<kIsSanitizeThread, ExclusiveMutex, SpinLock>;
+  folly::Synchronized<State, Mutex> state_;
 };
 
 inline SharedMutexFair::LockOperation<SharedMutexFair::LockAwaiter>
diff --git a/folly/folly/coro/SharedPromise.h b/folly/folly/coro/SharedPromise.h
--- a/folly/folly/coro/SharedPromise.h
+++ b/folly/folly/coro/SharedPromise.h
@@ -72,6 +72,13 @@
    */
   bool isFulfilled() const;
 
+  /*
+   * Returns either an optional holding the result or an empty optional
+   * depending on whether or not (respectively) the promise has been
+   * fulfilled (i.e., `isFulfilled() == true`).
+   */
+  std::optional<TryType> poll() const;
+
   /**
    * Sets an exception in the promise.
    */
@@ -148,6 +155,13 @@
 template <typename T>
 bool SharedPromise<T>::isFulfilled() const {
   return state_.withRLock([](auto& state) { return isFulfilled(state); });
+}
+
+template <typename T>
+auto SharedPromise<T>::poll() const -> std::optional<TryType> {
+  return state_.withRLock([](const auto& state) {
+    return isFulfilled(state) ? std::make_optional(state.result) : std::nullopt;
+  });
 }
 
 template <typename T>
diff --git a/folly/folly/coro/SmallUnboundedQueue.h b/folly/folly/coro/SmallUnboundedQueue.h
--- a/folly/folly/coro/SmallUnboundedQueue.h
+++ b/folly/folly/coro/SmallUnboundedQueue.h
@@ -73,7 +73,7 @@
         });
         co_await c.baton;
         if (cancelled) {
-          co_yield co_cancelled;
+          co_yield co_stopped_may_throw;
         }
       }
       buffer_ = queue_.getMessages();
diff --git a/folly/folly/coro/Synchronized.h b/folly/folly/coro/Synchronized.h
--- a/folly/folly/coro/Synchronized.h
+++ b/folly/folly/coro/Synchronized.h
@@ -190,10 +190,10 @@
   }
 
   template <typename FuncT>
-  using rlock_result_t = std::invoke_result_t<FuncT, ReadLockedPtr>;
+  using rlock_result_t = invoke_result_t<FuncT, ReadLockedPtr>;
 
   template <typename FuncT>
-  using wlock_result_t = std::invoke_result_t<FuncT, WriteLockedPtr>;
+  using wlock_result_t = invoke_result_t<FuncT, WriteLockedPtr>;
 
   template <typename FuncT, typename ReturnT = rlock_result_t<FuncT>>
   typename std::enable_if<!is_semi_awaitable_v<ReturnT>, Task<ReturnT>>::type
diff --git a/folly/folly/coro/Task.h b/folly/folly/coro/Task.h
--- a/folly/folly/coro/Task.h
+++ b/folly/folly/coro/Task.h
@@ -33,9 +33,8 @@
 #include <folly/ScopeGuard.h>
 #include <folly/Traits.h>
 #include <folly/Try.h>
-#include <folly/coro/AwaitImmediately.h>
+#include <folly/coro/BasePromise.h>
 #include <folly/coro/Coroutine.h>
-#include <folly/coro/CurrentExecutor.h>
 #include <folly/coro/Invoke.h>
 #include <folly/coro/Result.h>
 #include <folly/coro/ScopeExit.h>
@@ -49,6 +48,7 @@
 #include <folly/futures/Future.h>
 #include <folly/io/async/Request.h>
 #include <folly/lang/Assume.h>
+#include <folly/lang/MustUseImmediately.h>
 #include <folly/lang/SafeAlias-fwd.h>
 #include <folly/result/result.h>
 #include <folly/result/try.h>
@@ -74,17 +74,16 @@
   TaskPromisePrivate() = default;
 };
 
-class TaskPromiseBase {
+class TaskPromiseBase : public BasePromise<> {
   static TaskPromisePrivate privateTag() { return TaskPromisePrivate{}; }
 
+ protected:
   class FinalAwaiter {
    public:
     bool await_ready() noexcept { return false; }
 
     template <typename Promise>
-    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES coroutine_handle<>
-    await_suspend(coroutine_handle<Promise> coro) noexcept {
-      auto& promise = coro.promise();
+    coroutine_handle<> await_suspend_promise(Promise& promise) noexcept {
       // If ScopeExitTask has been attached, then we expect that the
       // ScopeExitTask will handle the lifetime of the async stack. See
       // ScopeExitTaskPromise's FinalAwaiter for more details.
@@ -114,24 +113,18 @@
       return promise.continuationRef(privateTag()).getHandle();
     }
 
+    template <typename Promise>
+    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES coroutine_handle<>
+    await_suspend(coroutine_handle<Promise> coro) noexcept {
+      return await_suspend_promise(coro.promise());
+    }
+
     [[noreturn]] void await_resume() noexcept { folly::assume_unreachable(); }
   };
 
-  friend class FinalAwaiter;
-
- protected:
   TaskPromiseBase() noexcept = default;
   ~TaskPromiseBase() = default;
 
-  template <typename Promise>
-  variant_awaitable<FinalAwaiter, ready_awaitable<>> do_safe_point(
-      Promise& promise) noexcept {
-    if (cancelToken_.isCancellationRequested()) {
-      return promise.yield_value(co_cancelled);
-    }
-    return ready_awaitable<>{};
-  }
-
  public:
   static void* operator new(std::size_t size) {
     return ::folly_coro_async_malloc(size);
@@ -145,60 +138,6 @@
 
   FinalAwaiter final_suspend() noexcept { return {}; }
 
-  template <
-      typename Awaitable,
-      std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
-  auto await_transform(Awaitable&& awaitable) {
-    bypassExceptionThrowing_ =
-        bypassExceptionThrowing_ == BypassExceptionThrowing::REQUESTED
-        ? BypassExceptionThrowing::ACTIVE
-        : BypassExceptionThrowing::INACTIVE;
-
-    return folly::coro::co_withAsyncStack(folly::coro::co_viaIfAsync(
-        executor_.get_alias(),
-        folly::coro::co_withCancellation(
-            cancelToken_, static_cast<Awaitable&&>(awaitable))));
-  }
-  template <
-      typename Awaitable,
-      std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
-  auto await_transform(Awaitable awaitable) {
-    bypassExceptionThrowing_ =
-        bypassExceptionThrowing_ == BypassExceptionThrowing::REQUESTED
-        ? BypassExceptionThrowing::ACTIVE
-        : BypassExceptionThrowing::INACTIVE;
-
-    return folly::coro::co_withAsyncStack(folly::coro::co_viaIfAsync(
-        executor_.get_alias(),
-        folly::coro::co_withCancellation(
-            cancelToken_,
-            mustAwaitImmediatelyUnsafeMover(std::move(awaitable))())));
-  }
-
-  template <typename Awaitable>
-  auto await_transform(NothrowAwaitable<Awaitable> awaitable) {
-    bypassExceptionThrowing_ = BypassExceptionThrowing::REQUESTED;
-    return await_transform(
-        mustAwaitImmediatelyUnsafeMover(awaitable.unwrap())());
-  }
-
-  auto await_transform(co_current_executor_t) noexcept {
-    return ready_awaitable<folly::Executor*>{executor_.get()};
-  }
-
-  auto await_transform(co_current_cancellation_token_t) noexcept {
-    return ready_awaitable<const folly::CancellationToken&>{cancelToken_};
-  }
-
-  void setCancelToken(folly::CancellationToken&& cancelToken) noexcept {
-    if (!hasCancelTokenOverride_) {
-      cancelToken_ = std::move(cancelToken);
-      hasCancelTokenOverride_ = true;
-    }
-  }
-
-  folly::AsyncStackFrame& getAsyncFrame() noexcept { return asyncFrame_; }
-
   folly::Executor::KeepAlive<> getExecutor() const noexcept {
     return executor_;
   }
@@ -206,10 +145,12 @@
   // These getters exist so that `FinalAwaiter` can interact with wrapped
   // `TaskPromise`s, and not just `TaskPromiseBase` descendants.  We use a
   // private tag to let `TaskWrapper` call them without becoming a `friend`.
-  auto& scopeExitRef(TaskPromisePrivate) { return scopeExit_; }
-  auto& continuationRef(TaskPromisePrivate) { return continuation_; }
+  auto& scopeExitRef(TaskPromisePrivate /*unused*/) { return scopeExit_; }
+  // FIXME: `result/coro.h` checks if this overload is callable to decide when
+  // something is a task-promise.  A second use-case would merit a concept.
+  auto& continuationRef(TaskPromisePrivate /*unused*/) { return continuation_; }
   // Unlike `getExecutor()`, does not copy an atomic.
-  auto& executorRef(TaskPromisePrivate) { return executor_; }
+  auto& executorRef(TaskPromisePrivate /*unused*/) { return executor_; }
 
  private:
   template <typename>
@@ -219,32 +160,22 @@
   friend class folly::coro::Task;
 
   friend coroutine_handle<ScopeExitTaskPromiseBase> tag_invoke(
-      cpo_t<co_attachScopeExit>,
+      cpo_t<co_attachScopeExit> /*unused*/,
       TaskPromiseBase& p,
       coroutine_handle<ScopeExitTaskPromiseBase> scopeExit) noexcept {
     return std::exchange(p.scopeExit_, scopeExit);
   }
 
-  ExtendedCoroutineHandle continuation_;
-  folly::AsyncStackFrame asyncFrame_;
-  folly::Executor::KeepAlive<> executor_;
-  folly::CancellationToken cancelToken_;
+  // From the base: continuation_, asyncFrame_, executor_, cancelToken_,
+  // hasCancelTokenOverride_
   coroutine_handle<ScopeExitTaskPromiseBase> scopeExit_;
-  bool hasCancelTokenOverride_ = false;
-
- protected:
-  enum class BypassExceptionThrowing : uint8_t {
-    INACTIVE,
-    ACTIVE,
-    REQUESTED,
-  } bypassExceptionThrowing_{BypassExceptionThrowing::INACTIVE};
 };
 
 // Separate from `TaskPromiseBase` so the compiler has less to specialize.
 template <typename Promise, typename T>
 class TaskPromiseCrtpBase
     : public TaskPromiseBase,
-      public ExtendedCoroutinePromise {
+      public ExtendedCoroutinePromiseCrtp<Promise> {
  public:
   using StorageType = detail::lift_lvalue_reference_t<T>;
 
@@ -266,31 +197,22 @@
     return final_suspend();
   }
 
-  using TaskPromiseBase::await_transform;
+  using BasePromise<>::await_transform;
 
-  auto await_transform(co_safe_point_t) noexcept {
-    return do_safe_point(*this);
+  auto await_transform(co_safe_point_t /*unused*/) noexcept {
+    return do_safe_point<FinalAwaiter>(*this);
   }
 
+  // Unlike `getErrorHandleUncheckedImpl`, checks the type of `me`.
+  static std::optional<ExtendedCoroutineHandle::ErrorHandle> getErrorHandleImpl(
+      Promise& me, exception_wrapper& ex) {
+    return getErrorHandleUncheckedImpl(me, ex);
+  }
+
  protected:
   TaskPromiseCrtpBase() noexcept = default;
   ~TaskPromiseCrtpBase() = default;
 
-  std::pair<ExtendedCoroutineHandle, AsyncStackFrame*> getErrorHandle(
-      exception_wrapper& ex) final {
-    auto& me = *static_cast<Promise*>(this);
-    if (bypassExceptionThrowing_ == BypassExceptionThrowing::ACTIVE) {
-      auto finalAwaiter = yield_value(co_error(std::move(ex)));
-      DCHECK(!finalAwaiter.await_ready());
-      return {
-          finalAwaiter.await_suspend(
-              coroutine_handle<Promise>::from_promise(me)),
-          // finalAwaiter.await_suspend pops a frame
-          getAsyncFrame().getParentFrame()};
-    }
-    return {coroutine_handle<Promise>::from_promise(me), nullptr};
-  }
-
   Try<StorageType> result_;
 };
 
@@ -354,14 +276,17 @@
 // This CPO deliberately does NOT use `tag_invoke`, but rather reuses the
 // `co_withExecutor` name as the ADL implementation, just like `co_viaIfAsync`.
 // The reason is that `tag_invoke()` would plumb through `Awaitable&&` instead
-// of `Awaitable`, but `must_await_immediately_v` types require by-value.
+// of `Awaitable`, but `folly::ext::must_use_immediately_v` types require
+// by-value.
 struct WithExecutorFunction {
   template <typename Awaitable>
   // Pass `awaitable` by-value, since `&&` would break immediate types
   auto operator()(Executor::KeepAlive<> executor, Awaitable awaitable) const
       FOLLY_DETAIL_FORWARD_BODY(co_withExecutor(
           std::move(executor),
-          mustAwaitImmediatelyUnsafeMover(std::move(awaitable))()))
+          // NOLINTNEXTLINE(facebook-folly-coro-temporary-by-ref)
+          folly::ext::must_use_immediately_unsafe_mover(
+              std::move(awaitable))()))
 };
 } // namespace adl
 
@@ -380,7 +305,7 @@
 ///
 /// More information on how to use this is available at folly::coro::Task.
 template <typename T>
-class FOLLY_NODISCARD TaskWithExecutor {
+class [[nodiscard]] TaskWithExecutor {
   using handle_t = coroutine_handle<detail::TaskPromise<T>>;
   using StorageType = typename detail::TaskPromise<T>::StorageType;
 
@@ -482,7 +407,7 @@
       F&& tryCallback,
       folly::CancellationToken cancelToken,
       void* returnAddress) && {
-    coro_.promise().setCancelToken(std::move(cancelToken));
+    coro_.promise().setCancellationToken(std::move(cancelToken));
     startImpl(std::move(*this), static_cast<F&&>(tryCallback))
         .start(returnAddress);
   }
@@ -492,12 +417,12 @@
       F&& tryCallback,
       folly::CancellationToken cancelToken,
       void* returnAddress) && {
-    coro_.promise().setCancelToken(std::move(cancelToken));
+    coro_.promise().setCancellationToken(std::move(cancelToken));
     // If the task replaces the request context and reaches a suspension point,
     // it will not have a chance to restore the previous context before we
     // return, so we need to ensure it is restored. This simulates starting the
     // coroutine in an actual executor, which would wrap the task with a guard.
-    RequestContextScopeGuard contextScope{RequestContext::saveContext()};
+    RequestContextSaverScopeGuard contextScope;
     startInlineImpl(std::move(*this), static_cast<F&&>(tryCallback))
         .start(returnAddress);
   }
@@ -634,7 +559,7 @@
 
     template <typename Promise>
     FOLLY_NOINLINE coroutine_handle<> await_suspend(
-        coroutine_handle<Promise> continuation) {
+        coroutine_handle<Promise> continuation) noexcept {
       DCHECK(coro_);
       auto& promise = coro_.promise();
       DCHECK(!promise.continuation_);
@@ -666,7 +591,8 @@
 
    private:
     friend InlineTryAwaitable tag_invoke(
-        cpo_t<co_withAsyncStack>, InlineTryAwaitable&& awaitable) noexcept {
+        cpo_t<co_withAsyncStack> /*unused*/,
+        InlineTryAwaitable&& awaitable) noexcept {
       return std::move(awaitable);
     }
 
@@ -699,22 +625,18 @@
   friend TaskWithExecutor co_withCancellation(
       folly::CancellationToken cancelToken, TaskWithExecutor&& task) noexcept {
     DCHECK(task.coro_);
-    task.coro_.promise().setCancelToken(std::move(cancelToken));
+    task.coro_.promise().setCancellationToken(std::move(cancelToken));
     return std::move(task);
   }
 
   friend TaskWithExecutor tag_invoke(
-      cpo_t<co_withAsyncStack>, TaskWithExecutor&& task) noexcept {
+      cpo_t<co_withAsyncStack> /*unused*/, TaskWithExecutor&& task) noexcept {
     return std::move(task);
   }
 
-  NoOpMover<TaskWithExecutor> getUnsafeMover(
-      ForMustAwaitImmediately) && noexcept {
-    return NoOpMover{std::move(*this)}; // Asserts `this` is nothrow-movable
-  }
-
   using folly_private_task_without_executor_t = Task<T>;
-  // See comment in `Task`, or use `SafeTaskWithExecutor` instead.
+  // See comment in `Task`, or use `safe_task_with_executor` instead.
+  template <safe_alias>
   using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;
 
  private:
@@ -728,7 +650,7 @@
 // This macro makes it easier for `TaskWrapper.h` users to apply the correct
 // attributes for the wrapped `Task`s.
 #define FOLLY_CORO_TASK_ATTRS \
-  FOLLY_NODISCARD [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]
+  [[nodiscard]] [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]
 
 /// Represents an allocated, but not-started coroutine, which is not yet
 /// been bound to an executor.
@@ -805,7 +727,7 @@
   /// Specify the executor that this task should execute on:
   ///   co_withExecutor(executor, std::move(task))
   //
-  /// @param executor An Executor::KeepAlive object, which can be implicity
+  /// @param executor An Executor::KeepAlive object, which can be implicitly
   /// constructed from Executor*
   /// @returns a new TaskWithExecutor object, which represents the existing Task
   /// bound to an executor
@@ -859,28 +781,29 @@
   friend Task co_withCancellation(
       folly::CancellationToken cancelToken, Task&& task) noexcept {
     DCHECK(task.coro_);
-    task.coro_.promise().setCancelToken(std::move(cancelToken));
+    task.coro_.promise().setCancellationToken(std::move(cancelToken));
     return std::move(task);
   }
 
   template <typename F, typename... A, typename F_, typename... A_>
   friend Task tag_invoke(
-      tag_t<co_invoke_fn>, tag_t<Task, F, A...>, F_ f, A_... a) {
-    co_yield co_result(co_await co_awaitTry(
-        invoke(static_cast<F&&>(f), static_cast<A&&>(a)...)));
-  }
-
-  NoOpMover<Task> getUnsafeMover(ForMustAwaitImmediately) && noexcept {
-    return NoOpMover{std::move(*this)}; // Asserts `this` is nothrow-movable
+      tag_t<co_invoke_fn> /*unused*/,
+      tag_t<Task, F, A...> /*unused*/,
+      F_ f,
+      A_... a) {
+    co_yield co_result(
+        co_await co_awaitTry(
+            invoke(static_cast<F&&>(f), static_cast<A&&>(a)...)));
   }
 
   using PrivateAwaiterTypeForTests = Awaiter;
-  // Use `SafeTask` instead of `Task` to move tasks into other safe coro APIs.
+  // Use `safe_task` instead of `Task` to move tasks into other safe coro APIs.
   //
   // User-facing stuff from `Task.h` can trivially include unsafe aliasing, the
   // `folly::coro` docs include hundreds of words of pitfalls.  The intent here
   // is to catch people accidentally passing `Task`s into safer primitives, and
   // breaking their memory-safety guarantees.
+  template <safe_alias>
   using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;
 
  private:
@@ -957,7 +880,7 @@
     // This overload needed as Awaiter is returned from co_viaIfAsync() which is
     // then passed into co_withAsyncStack().
     friend Awaiter tag_invoke(
-        cpo_t<co_withAsyncStack>, Awaiter&& awaiter) noexcept {
+        cpo_t<co_withAsyncStack> /*unused*/, Awaiter&& awaiter) noexcept {
       return std::move(awaiter);
     }
 
@@ -981,7 +904,7 @@
   co_return;
 }
 /// Same as makeTask(). See Unit
-inline Task<void> makeTask(Unit) {
+inline Task<void> makeTask(Unit /*unused*/) {
   co_return;
 }
 
@@ -1004,6 +927,10 @@
 template <typename Promise, typename T>
 inline Task<T>
 detail::TaskPromiseCrtpBase<Promise, T>::get_return_object() noexcept {
+  // Watch out: When used with `TaskWrapper`, this relies on "practically safe"
+  // UB wherein this handle is only valid because `TaskPromise` and the true
+  // "wrapper promise" of the wrapper coro coincide in layout exactly.
+  // Documented in `TaskPromiseWrapperBase::is_promise_type_punning_safe`.
   return Task<T>{
       coroutine_handle<Promise>::from_promise(*static_cast<Promise*>(this))};
 }
diff --git a/folly/folly/coro/TaskWrapper.h b/folly/folly/coro/TaskWrapper.h
--- a/folly/folly/coro/TaskWrapper.h
+++ b/folly/folly/coro/TaskWrapper.h
@@ -16,8 +16,8 @@
 
 #pragma once
 
-#include <folly/coro/AwaitImmediately.h>
 #include <folly/coro/Task.h>
+#include <folly/lang/MustUseImmediately.h>
 
 /// `TaskWrapper.h` provides base classes for wrapping `folly::coro::Task` with
 /// custom functionality.  These work by composition, which avoids the pitfalls
@@ -44,43 +44,44 @@
 ///
 /// ### WARNING: Do not blindly forward more APIs in `TaskWrapper.h`!
 ///
-/// Several existing wrappers are immediately-awaitable (`AwaitImmediately.h`).
-/// For those tasks (e.g. `NowTask`), API forwarding is risky:
+/// Several existing wrappers are immediately-awaitable (see
+/// `MustUseImmediately.h`).  For those tasks (e.g. `now_task`), API
+/// forwarding is risky:
 ///   - Do NOT forward `semi()`, `start*()`, `unwrap()`, or other methods, or
 ///     CPOs that take the awaitable by-reference.  All of those make it
 ///     trivial to accidentally break the immediately-awaitable invariant, and
 ///     cause lifetime bugs.
 ///   - When forwarding an API, use either a static method or CPO.  Then,
 ///     either ONLY take the awaitable by-value, or bifurcate the API on
-///     `must_await_immediately_v<Awaitable>`, grep for examples.
+///     `folly::ext::must_use_immediately_v<Awaitable>`, grep for examples.
 ///
 /// If you **have** to forward an unsafe API, here are some suggestions:
 ///   - Only add them in your wrapper.
 ///   - Add them via `UnsafeTaskWrapperCrtp` deriving from `TaskWrapperCrtp`.
 ///   - Add boolean flags to the configuration struct, and gate the methods via
 ///     `enable_if`.  NB: You probably cannot gate these on `Derived` **not**
-///     being `MustAwaitImmediately`, since CRTP bases see an incomplete type.
+///     being must-use-immediately, since CRTP bases see an incomplete type.
 ///
 /// ### WARNING: Beware of object slicing in "unwrapping" APIs
 ///
-/// Start by reading "A note on object slicing" in `AwaitImmediately.h`.
+/// Start by reading "A note on object slicing" in `MustUseImmediately.h`.
 ///
 /// If your wrapper is adding new members, or customizing object lifecycle
 /// (dtor / copy / move / assignment), then you must:
-///   - Write a custom `getUnsafeMover()`.
+///   - Write a custom `unsafe_mover()` as per `lang/MustUseImmediately.h`.
 ///   - Overload the protected `unsafeTask()` and `unsafeTaskWithExecutor()` to
 ///     reduce slicing risk.
 ///   - Take care not to slice down to the `Crtp` bases.
 ///
 /// ### How to implement a wrapper
 ///
-/// First, read the WARNINGs above. Then, follow one of the "Tiny" examples
-/// in `TaskWrapperTest.cpp`. The important things are:
+/// First, read the WARNING sections above. Then, follow one of the "Tiny"
+/// examples in `TaskWrapperTest.cpp`. The important things are:
 ///   - Actually read the "object slicing" warning above!
 ///   - In most cases, you'll need to both implement a task, and customize its
 ///     `TaskWithExecutorT`.  If you leave that as `coro::TaskWithExecutor`,
 ///     some users will accidentally avoid your wrapper's effects.
-///   - Tag `YourTaskWithExecutor` with `FOLLY_NODISCARD`.
+///   - Tag `YourTaskWithExecutor` with `[[nodiscard]]`.
 ///   - Tag `YourTask` with the `FOLLY_CORO_TASK_ATTRS` attribute.  Caveat:
 ///     This assumes that the coro's caller will outlive it.  That is true for
 ///     `Task`, and almost certainly true of all sensible wrapper types.
@@ -121,6 +122,36 @@
 
 template <typename T, typename WrapperTask, typename Promise>
 class TaskPromiseWrapperBase {
+ private:
+  // Detail of `is_promise_type_punning_safe`. Implementation notes:
+  //  - This needs a body because GCC doesn't want `d` referenced in a `->` type
+  //    signature.
+  //  - To use this with the cheaper-to-compile `FOLLY_DECLVAL`, which is
+  //    `nullptr`, this must be in an unevaluated context, since patently-null
+  //    static casts are special in that they discard offsets.  So, the below
+  //    equality would always be true during constant evaluation.
+  template <typename Me>
+  static consteval auto promise_at_offset0(Me me) {
+    return std::bool_constant<
+        static_cast<const void*>(&me) ==
+        static_cast<const void*>(&me.promise_)>{};
+  }
+
+  // CRITICAL SAFETY CHECK: `&promise_` must be the same as `this`, and both
+  // objects must have the same size.  This is required since some promise
+  // operations (notably `get_return_object()` need to obtain a
+  // `coroutine_handle<Promise>` to allow the wrapped coro not to know about
+  // the wrapper.  At the same time, the actual promise is
+  // `TaskPromiseWrapperBase`.  Punning promises & handles in this way is
+  // technically UB, but it's practically safe so long as the layouts of
+  // `Promise` and `TaskPromiseWrapperBase` are identical, which is what we
+  // verify here.
+  static consteval bool is_promise_type_punning_safe() {
+    return require_sizeof<Promise> == require_sizeof<TaskPromiseWrapperBase> &&
+        decltype(promise_at_offset0(
+            FOLLY_DECLVAL(TaskPromiseWrapperBase)))::value;
+  }
+
  protected:
   static_assert(
       is_task_or_wrapper_v<WrapperTask, T>,
@@ -138,6 +169,11 @@
   using TaskWrapperInnerPromise = Promise;
 
   WrapperTask get_return_object() noexcept {
+    // CRITICAL: This assert justifies why it is practically safe to rely on
+    // the `from_promise` UB in `TaskPromiseCrtpBase::get_return_object`.
+    //
+    // PS The assert isn't at class scope, since the type would be incomplete.
+    static_assert(is_promise_type_punning_safe());
     return WrapperTask{promise_.get_return_object()};
   }
 
@@ -153,16 +189,16 @@
 
   template <
       typename Awaitable,
-      std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
+      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
   auto await_transform(Awaitable&& what) {
     return promise_.await_transform(std::forward<Awaitable>(what));
   }
   template <
       typename Awaitable,
-      std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
+      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
   auto await_transform(Awaitable what) {
     return promise_.await_transform(
-        mustAwaitImmediatelyUnsafeMover(std::move(what))());
+        folly::ext::must_use_immediately_unsafe_mover(std::move(what))());
   }
 
   auto yield_value(auto&& v)
@@ -185,6 +221,16 @@
   auto& executorRef(TaskPromisePrivate tag) {
     return promise_.executorRef(tag);
   }
+
+  // These next two definitions forward `getErrorHandle` behavior to the
+  // innermost promise object.  See `ExtendedCoroutineHandle` for docs.
+
+  using use_extended_handle_concept = ExtendedCoroutineHandle::PrivateTag;
+
+  static ExtendedCoroutineHandle::PromiseBase* getPromiseBase(
+      ExtendedCoroutineHandle::PrivateTag priv, TaskPromiseWrapperBase* me) {
+    return Promise::getPromiseBase(priv, &me->promise_);
+  }
 };
 
 template <typename T, typename WrapperTask, typename Promise>
@@ -197,6 +243,9 @@
  public:
   template <typename U = T> // see "`co_return` with implicit ctor" test
   auto return_value(U&& value) {
+    static_assert( // See `is_promise_type_punning_safe` for rationale
+        require_sizeof<TaskPromiseWrapper> ==
+        require_sizeof<TaskPromiseWrapperBase<T, WrapperTask, Promise>>);
     return this->promise_.return_value(std::forward<U>(value));
   }
 };
@@ -209,7 +258,12 @@
   ~TaskPromiseWrapper() = default;
 
  public:
-  void return_void() noexcept { this->promise_.return_void(); }
+  void return_void() noexcept {
+    static_assert( // See `is_promise_type_punning_safe` for rationale
+        require_sizeof<TaskPromiseWrapper> ==
+        require_sizeof<TaskPromiseWrapperBase<void, WrapperTask, Promise>>);
+    this->promise_.return_void();
+  }
 };
 
 // Mixin for TaskWrapper.h configs for `Task` & `TaskWithExecutor` types
@@ -250,30 +304,23 @@
       Executor::KeepAlive<> executor, Derived tw) noexcept {
     return Cfg::wrapAwaitable(co_viaIfAsync(
         std::move(executor),
-        mustAwaitImmediatelyUnsafeMover(std::move(tw).unwrapTask())()));
+        folly::ext::must_use_immediately_unsafe_mover(
+            std::move(tw).unwrapTask())()));
   }
 
   // No `cpo_t<co_withAsyncStack>` since a "Task" is not an awaitable.
 
-  auto getUnsafeMover(ForMustAwaitImmediately p) && noexcept {
-    // See "A note on object slicing" above `mustAwaitImmediatelyUnsafeMover`
-    static_assert(sizeof(Derived) == sizeof(typename Cfg::InnerTaskT));
-    static_assert( // More `noexcept` tests in `MustAwaitImmediatelyUnsafeMover`
-        noexcept(std::move(*this).unwrapTask().getUnsafeMover(p)));
-    return MustAwaitImmediatelyUnsafeMover{
-        (Derived*)nullptr, std::move(*this).unwrapTask().getUnsafeMover(p)};
-  }
-
   using folly_private_task_wrapper_inner_t = typename Cfg::InnerTaskT;
   using folly_private_task_wrapper_crtp_base = TaskWrapperCrtp;
 
   // Wrappers can override these as-needed
-  using folly_private_must_await_immediately_t =
-      must_await_immediately_t<typename Cfg::InnerTaskT>;
-  using folly_private_noexcept_awaitable_t =
-      noexcept_awaitable_t<typename Cfg::InnerTaskT>;
+  using folly_must_use_immediately_t =
+      ext::must_use_immediately_t<typename Cfg::InnerTaskT>;
+  using folly_private_value_only_awaitable_t =
+      value_only_awaitable_t<typename Cfg::InnerTaskT>;
+  template <safe_alias Default>
   using folly_private_safe_alias_t =
-      safe_alias_of<folly_private_task_wrapper_inner_t>;
+      safe_alias_of<folly_private_task_wrapper_inner_t, Default>;
 
  private:
   using Inner = folly_private_task_wrapper_inner_t;
@@ -281,31 +328,61 @@
       detail::is_task_or_wrapper_v<Inner, typename Cfg::ValueT>,
       "*TaskWrapper must wrap a sequence of wrappers ending in Task<T>");
 
-  Inner task_;
+  Inner inner_;
 
  protected:
   template <typename, typename, typename> // can construct
   friend class ::folly::coro::detail::TaskPromiseWrapperBase;
-  friend class MustAwaitImmediatelyUnsafeMover< // can construct
-      Derived,
-      detail::unsafe_mover_for_must_await_immediately_t<Inner>>;
 
-  explicit TaskWrapperCrtp(Inner t)
-      // `mustAwaitImmediatelyUnsafeMover` has more `noexcept` assertions.
-      noexcept(noexcept(Inner{FOLLY_DECLVAL(Inner)}))
-      : task_(mustAwaitImmediatelyUnsafeMover(std::move(t))()) {
+  explicit TaskWrapperCrtp(Inner t) noexcept
+      // NOLINTNEXTLINE(facebook-folly-coro-temporary-by-ref)
+      : inner_(folly::ext::must_use_immediately_unsafe_mover(std::move(t))()) {
     static_assert(
-        must_await_immediately_v<Derived> ||
-            !must_await_immediately_v<typename Cfg::TaskWithExecutorT>,
-        "`TaskWithExecutorT` must `AddMustAwaitImmediately` because the inner "
+        folly::ext::must_use_immediately_v<Derived> ||
+            !folly::ext::must_use_immediately_v<
+                typename Cfg::TaskWithExecutorT>,
+        "`TaskWithExecutorT` must `ext::wrap_must_use_immediately_t` because the inner "
         "task did");
   }
 
-  // See "A note on object slicing" above `mustAwaitImmediatelyUnsafeMover`
+  // See "A note on object slicing" in `MustUseImmediately.h`
   Inner unwrapTask() && noexcept {
     static_assert(sizeof(Inner) == sizeof(Derived));
-    return mustAwaitImmediatelyUnsafeMover(std::move(task_))();
+    return folly::ext::must_use_immediately_unsafe_mover(std::move(inner_))();
   }
+
+ private:
+  template <typename T>
+  using my_curried_mover = folly::ext::curried_unsafe_mover_t<
+      T,
+      decltype(folly::ext::must_use_immediately_unsafe_mover(
+          FOLLY_DECLVAL(Inner)))>;
+
+ public:
+  template <
+      typename Me, // not a forwarding ref, see SFINAE
+      // This check guards against misuse (+ fails on lvalue refs)
+      // See `wrap_must_use_immediately_t::unsafe_mover` for more context
+      std::enable_if_t<std::is_base_of_v<TaskWrapperCrtp, Me>, int> = 0>
+  static my_curried_mover<Me> unsafe_mover(
+      folly::ext::must_use_immediately_private_t /*unused*/, Me&& me) noexcept {
+    return folly::ext::curried_unsafe_mover_from_bases_and_members<
+        TaskWrapperCrtp>(
+        folly::tag</*no bases*/>,
+        folly::vtag<&TaskWrapperCrtp::inner_>,
+        static_cast<Me&&>(me));
+  }
+  template <
+      typename DerivedFromMe,
+      // Matches the SFINAE logic in our `unsafe_mover`
+      std::enable_if_t<std::is_base_of_v<TaskWrapperCrtp, DerivedFromMe>, int> =
+          0>
+  explicit TaskWrapperCrtp(
+      folly::ext::curried_unsafe_mover_private_t /*unused*/,
+      my_curried_mover<DerivedFromMe>&& mover)
+      // `must_use_immediately_unsafe_mover` has more `noexcept` assertions
+      noexcept(noexcept(Inner{std::move(mover.template get<0>())()}))
+      : inner_{std::move(mover.template get<0>())()} {}
 };
 
 // IMPORTANT: Read "Do not blindly forward more APIs" in the file docblock.  In
@@ -318,23 +395,18 @@
   Inner inner_;
 
  protected:
-  friend class MustAwaitImmediatelyUnsafeMover< // can construct
-      Derived,
-      detail::unsafe_mover_for_must_await_immediately_t<Inner>>;
-
-  // See "A note on object slicing" above `mustAwaitImmediatelyUnsafeMover`
+  // See "A note on object slicing" in `MustUseImmediately.h`
   Inner unwrapTaskWithExecutor() && noexcept {
     static_assert(sizeof(Inner) == sizeof(Derived));
-    return mustAwaitImmediatelyUnsafeMover(std::move(inner_))();
+    return folly::ext::must_use_immediately_unsafe_mover(std::move(inner_))();
   }
 
   // Our task can construct us, and that logic lives in the CRTP base
   friend typename Cfg::WrapperTaskT::folly_private_task_wrapper_crtp_base;
 
-  explicit TaskWithExecutorWrapperCrtp(Inner t)
-      // `mustAwaitImmediatelyUnsafeMover` has more `noexcept` assertions.
-      noexcept(noexcept(Inner{FOLLY_DECLVAL(Inner)}))
-      : inner_(mustAwaitImmediatelyUnsafeMover(std::move(t))()) {}
+  explicit TaskWithExecutorWrapperCrtp(Inner t) noexcept(noexcept(Inner{
+      FOLLY_DECLVAL(Inner)}))
+      : inner_(folly::ext::must_use_immediately_unsafe_mover(std::move(t))()) {}
 
  public:
   // This is a **deliberately undefined** declaration. It is provided so that
@@ -364,7 +436,8 @@
       CancellationToken cancelToken, Derived twe) noexcept {
     return Derived{co_withCancellation(
         std::move(cancelToken),
-        mustAwaitImmediatelyUnsafeMover(std::move(twe.inner_))())};
+        folly::ext::must_use_immediately_unsafe_mover(
+            std::move(twe.inner_))())};
   }
 
   // Pass `twe` by-value, since `&&` would break immediately-awaitable types
@@ -373,7 +446,8 @@
       Executor::KeepAlive<> executor, Derived twe) noexcept {
     return Cfg::wrapAwaitable(co_viaIfAsync(
         std::move(executor),
-        mustAwaitImmediatelyUnsafeMover(std::move(twe.inner_))()));
+        folly::ext::must_use_immediately_unsafe_mover(
+            std::move(twe.inner_))()));
   }
 
   // `AsyncScope` requires an awaitable with an executor already attached, and
@@ -381,13 +455,13 @@
   // we still need to wrap the awaitable on that code path.
   //
   // NB: Passing by-&& here looks like it could compromise the safety of
-  // immediately-awaitable coros (`NowTask`, `NowTaskWithExecutor`).  With
+  // immediately-awaitable coros (`now_task`, `now_task_with_executor`).  With
   // by-value, `BlockingWaitTest.AwaitNowTaskWithExecutor` would not build.
   //
   // Supporting pass-by-value would require fixing a LOT of plumbing.
   //   - `WithAsyncStack.h` calls `is_tag_invocable_v`, which would fail on
-  //     `NowTaskWithExecutor` if this is by-value, since the implementation of
-  //     `is_tag_invocable_v` presents all args by-&&.
+  //     `now_task_with_executor` if this is by-value, since the implementation
+  //     of `is_tag_invocable_v` presents all args by-&&.
   //   - `CommutativeWrapperAwaitable` and `StackAwareViaIfAsyncAwaiter`,
   //     among others, also assume that `co_withAsyncStack` takes by-ref.
   //
@@ -396,24 +470,52 @@
   // never be called in user code.  Internal usage in `folly/coro` looks
   // overall immediately-awaitable-safe -- and the best safeguard for any
   // particular scenario is to test, see e.g. `NowTaskTest.blockingWait`.
-  friend auto tag_invoke(cpo_t<co_withAsyncStack>, Derived&& twe) noexcept(
+  friend auto
+  tag_invoke(cpo_t<co_withAsyncStack> /*unused*/, Derived&& twe) noexcept(
       noexcept(co_withAsyncStack(FOLLY_DECLVAL(Inner)))) {
     return Cfg::wrapAwaitable(co_withAsyncStack(std::move(twe.inner_)));
   }
 
-  auto getUnsafeMover(ForMustAwaitImmediately p) && noexcept {
-    // See "A note on object slicing" above `mustAwaitImmediatelyUnsafeMover`
-    static_assert(sizeof(Derived) == sizeof(Inner));
-    static_assert( // More `noexcept` tests in `MustAwaitImmediatelyUnsafeMover`
-        noexcept(std::move(inner_).getUnsafeMover(p)));
-    return MustAwaitImmediatelyUnsafeMover{
-        (Derived*)nullptr, std::move(inner_).getUnsafeMover(p)};
-  }
-
-  using folly_private_must_await_immediately_t =
-      must_await_immediately_t<Inner>;
+  using folly_must_use_immediately_t = ext::must_use_immediately_t<Inner>;
   using folly_private_task_without_executor_t = typename Cfg::WrapperTaskT;
-  using folly_private_safe_alias_t = safe_alias_of<Inner>;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t = safe_alias_of<Inner, Default>;
+
+ private:
+  template <typename T>
+  using my_curried_mover = folly::ext::curried_unsafe_mover_t<
+      T,
+      decltype(folly::ext::must_use_immediately_unsafe_mover(
+          FOLLY_DECLVAL(Inner)))>;
+
+ public:
+  template <
+      typename Me, // not a forwarding ref, see SFINAE
+      // This check guards against misuse (+ fails on lvalue refs)
+      // See `wrap_must_use_immediately_t::unsafe_mover` for more context
+      std::enable_if_t<
+          std::is_base_of_v<TaskWithExecutorWrapperCrtp, Me>,
+          int> = 0>
+  static my_curried_mover<Me> unsafe_mover(
+      folly::ext::must_use_immediately_private_t /*unused*/, Me&& me) noexcept {
+    return folly::ext::curried_unsafe_mover_from_bases_and_members<
+        TaskWithExecutorWrapperCrtp>(
+        folly::tag</*no bases*/>,
+        folly::vtag<&TaskWithExecutorWrapperCrtp::inner_>,
+        static_cast<Me&&>(me));
+  }
+  template <
+      typename DerivedFromMe,
+      // Matches the SFINAE logic in our `unsafe_mover`
+      std::enable_if_t<
+          std::is_base_of_v<TaskWithExecutorWrapperCrtp, DerivedFromMe>,
+          int> = 0>
+  explicit TaskWithExecutorWrapperCrtp(
+      folly::ext::curried_unsafe_mover_private_t /*unused*/,
+      my_curried_mover<DerivedFromMe>&& mover)
+      // `must_use_immediately_unsafe_mover` has more `noexcept` assertions
+      noexcept(noexcept(Inner{std::move(mover.template get<0>())()}))
+      : inner_{std::move(mover.template get<0>())()} {}
 };
 
 } // namespace folly::coro
diff --git a/folly/folly/coro/Timeout-inl.h b/folly/folly/coro/Timeout-inl.h
--- a/folly/folly/coro/Timeout-inl.h
+++ b/folly/folly/coro/Timeout-inl.h
@@ -79,8 +79,8 @@
 
   exception_wrapper error;
   try {
-    auto resultTry =
-        co_await folly::coro::co_awaitTry(folly::coro::co_withCancellation(
+    auto resultTry = co_await folly::coro::co_awaitTry(
+        folly::coro::co_withCancellation(
             cancelSource.getToken(), std::move(semiFn)()));
 
     cancelCallback.reset();
@@ -132,7 +132,7 @@
 typename detail::TimeoutTask<SemiAwaitable, TimekeeperPtr> timeout(
     SemiAwaitable semiAwaitable, Duration timeoutDuration, TimekeeperPtr tk) {
   return detail::timeoutImpl<SemiAwaitable, Duration, /*discard=*/true>(
-      mustAwaitImmediatelyUnsafeMover(std::move(semiAwaitable)),
+      folly::ext::must_use_immediately_unsafe_mover(std::move(semiAwaitable)),
       timeoutDuration,
       std::move(tk));
 }
@@ -141,7 +141,7 @@
 typename detail::TimeoutTask<SemiAwaitable, TimekeeperPtr> timeoutNoDiscard(
     SemiAwaitable semiAwaitable, Duration timeoutDuration, TimekeeperPtr tk) {
   return detail::timeoutImpl<SemiAwaitable, Duration, /*discard=*/false>(
-      mustAwaitImmediatelyUnsafeMover(std::move(semiAwaitable)),
+      folly::ext::must_use_immediately_unsafe_mover(std::move(semiAwaitable)),
       timeoutDuration,
       std::move(tk));
 }
diff --git a/folly/folly/coro/Timeout.h b/folly/folly/coro/Timeout.h
--- a/folly/folly/coro/Timeout.h
+++ b/folly/folly/coro/Timeout.h
@@ -21,9 +21,9 @@
 #include <folly/coro/Traits.h>
 #include <folly/coro/detail/PickTaskWrapper.h>
 #include <folly/futures/Future.h>
-// `timeout(coroFutureInt())` makes a `SafeTask`
+// `timeout(coroFutureInt())` makes a `safe_task`
 #include <folly/coro/safe/SafeTask.h>
-// `timeout(memberTask())` makes a `NowTask`
+// `timeout(memberTask())` makes a `now_task`
 #include <folly/coro/safe/NowTask.h>
 
 #if FOLLY_HAS_COROUTINES
@@ -31,14 +31,16 @@
 namespace folly::coro {
 
 namespace detail {
-// This doesn't try to apply `AsNoexcept` to the output, since `timeout` is
+// This doesn't try to apply `value_or_fatal` to the output, since `timeout` is
 // expected to throw, and `timeoutNoDiscard()` may either complete with a
 // stopped state, or with an error.
 template <typename SemiAwaitable, typename TimekeeperPtr>
-using TimeoutTask = PickTaskWrapper<
+using TimeoutTask = pick_task_wrapper<
     typename semi_await_try_result_t<SemiAwaitable>::element_type,
-    std::min(safe_alias_of_v<TimekeeperPtr>, safe_alias_of_v<SemiAwaitable>),
-    must_await_immediately_v<SemiAwaitable>>;
+    std::min(
+        lenient_safe_alias_of_v<TimekeeperPtr>,
+        lenient_safe_alias_of_v<SemiAwaitable>),
+    folly::ext::must_use_immediately_v<SemiAwaitable>>;
 } // namespace detail
 
 /// Returns a Task that, when started, starts a timer of duration
@@ -65,7 +67,7 @@
     typename SemiAwaitable,
     typename Duration,
     // Templated so we can take safe pointers like `capture<Timekeeper&>` from
-    // `folly/coro/safe`, and return a `SafeTask`.
+    // `folly/coro/safe`, and return a `safe_task`.
     typename TimekeeperPtr = std::nullptr_t>
 typename detail::TimeoutTask<SemiAwaitable, TimekeeperPtr> timeout(
     SemiAwaitable semiAwaitable,
diff --git a/folly/folly/coro/UnboundedQueue.h b/folly/folly/coro/UnboundedQueue.h
--- a/folly/folly/coro/UnboundedQueue.h
+++ b/folly/folly/coro/UnboundedQueue.h
@@ -28,7 +28,12 @@
 
 // Wrapper around folly::UnboundedQueue with async wait
 
-template <typename T, bool SingleProducer = false, bool SingleConsumer = false>
+template <
+    typename T,
+    bool SingleProducer = false,
+    bool SingleConsumer = false,
+    bool MayBlock = false,
+    size_t LgSegmentSize = 8>
 class UnboundedQueue {
  public:
   template <typename U = T>
@@ -87,7 +92,13 @@
   size_t size() const { return queue_.size(); }
 
  private:
-  folly::UnboundedQueue<T, SingleProducer, SingleConsumer, false> queue_;
+  folly::UnboundedQueue< //
+      T,
+      SingleProducer,
+      SingleConsumer,
+      MayBlock,
+      LgSegmentSize>
+      queue_;
   folly::fibers::Semaphore sem_{0};
 };
 
diff --git a/folly/folly/coro/ValueOrError.h b/folly/folly/coro/ValueOrError.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/coro/ValueOrError.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/coro/ViaIfAsync.h>
+#include <folly/result/try.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly::coro {
+
+/// `value_or_error_or_stopped` is the `result<T>` analog of the older
+/// `co_awaitTry`.
+///
+/// In a `folly::coro` async coroutine, use `value_or_error_or_stopped` like so:
+///
+///   result<int> res = co_await value_or_error_or_stopped(taskReturningInt());
+///   if (auto ex = get_exception<MyError>(res)) {
+///     /* handle ex */
+///   } else {
+///     sum += co_await co_ready(res); // efficiently propagate unhandled error
+///   }
+///
+/// Contrast that with related async coro vocabulary:
+///  - `co_yield co_result(r)` from `Result.h` -- propagate `result<T>` or
+///    `Try<T>` to the awaiter of the current coro.
+///  - `auto& v = co_await co_ready(r)` from `Ready.h` -- given a `result<T>`,
+///    unpack the value, or propagate any error to our awaiter.
+///
+/// The purpose of `value_or_error_or_stopped` is to handle errors from a child
+/// task via `result<T>`, rather than through `try {} catch {}`.  Some reasons
+/// to do so:
+///   - Your error-handling APIs (logging, retry, etc) use `result<T>`.
+///   - You wish to avoid the ~microsecond cost of thrown exceptions,
+///     applicable only when your error path is hot, and the child uses
+///     `co_yield` instead of `throw` to propagate exceptions.
+
+namespace detail {
+
+template <typename Awaiter>
+using detect_await_resume_result =
+    decltype(FOLLY_DECLVAL(Awaiter).await_resume_result());
+
+template <typename Awaiter>
+constexpr bool is_awaiter_result =
+    is_detected_v<detect_await_resume_result, Awaiter>;
+
+template <typename Awaitable>
+constexpr bool is_awaitable_result =
+    is_awaiter_result<awaiter_type_t<Awaitable>>;
+
+// On the happy path, this uses the dedicated `await_resume_result()` protocol,
+// if it's supported by the awaiter.
+//
+// As fallback, this reuses the `await_resume_try()` machinery in the hope that
+// the compiler will be able to optimize away the `Try` -> `result` conversion.
+//
+// The reasons to support the dedicated protocol are (1) better semantics, and
+// (2) a data flow that's easier for the compiler to optimize.  Specifically:
+//
+//   - `await_resume_result()` cleanly handles `Task<V&>`, whereas `Try`
+//     doesn't support storing references, and the caller of `co_awaitTry` has
+//     to deal with `Try<std::reference_wrapper<V>>`.  See the test in
+//     `TaskOfLvalueReferenceAsTry`.
+//
+//   - `await_resume_result()` implementations can explicitly avoid the "empty
+//     `Try`" pitfall, which is something that gets converted to a
+//     `UsingUninitializedTry` error by the `try_to_result()` fallback.
+//
+//   - Falling back to `await_resume_try()` can incur an extra move-copy, which
+//     may not always optimize away.
+template <typename Awaitable>
+class ResultAwaiter : public ValueOnlyAwaiterBase<Awaitable> {
+ private:
+  static_assert(is_awaitable_try<Awaitable> || is_awaitable_result<Awaitable>);
+
+  using Base = ValueOnlyAwaiterBase<Awaitable>;
+
+ public:
+  explicit ResultAwaiter(Awaitable&& awaitable)
+      : Base(static_cast<Awaitable&&>(awaitable)) {}
+
+  // Preferred `await_resume()`: use `await_resume_result()` when available
+  template <
+      typename Awaiter2 = typename Base::Awaiter,
+      typename Result =
+          decltype(FOLLY_DECLVAL(Awaiter2&).await_resume_result())>
+  Result await_resume() noexcept(
+      noexcept(this->awaiter_.await_resume_result())) {
+    return this->awaiter_.await_resume_result();
+  }
+
+  // Fallback `await_resume()`: use `await_resume_try()` otherwise
+  template <
+      typename Awaiter2 = typename Base::Awaiter,
+      typename Result =
+          decltype(try_to_result(FOLLY_DECLVAL(Awaiter2&).await_resume_try()))>
+  Result await_resume() noexcept(
+      noexcept(try_to_result(this->awaiter_.await_resume_try())))
+    requires(!is_awaitable_result<Awaitable>) // Fallback logic
+  {
+    return try_to_result(this->awaiter_.await_resume_try());
+  }
+};
+
+template <typename T>
+class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] ValueOrErrorOrStopped
+    : public CommutativeWrapperAwaitable<ValueOrErrorOrStopped, T> {
+ public:
+  using CommutativeWrapperAwaitable<ValueOrErrorOrStopped, T>::
+      CommutativeWrapperAwaitable;
+
+  template <
+      typename Self,
+      std::enable_if_t<
+          std::is_same_v<remove_cvref_t<Self>, ValueOrErrorOrStopped>,
+          int> = 0,
+      typename T2 = like_t<Self, T>,
+      std::enable_if_t<is_awaitable_v<T2>, int> = 0>
+  friend ResultAwaiter<T2> operator co_await(Self&& self) {
+    return ResultAwaiter<T2>{static_cast<Self&&>(self).inner_};
+  }
+
+  using folly_private_value_only_awaitable_t = std::true_type;
+};
+
+// The awaitable backing `ValueOrError`, see that doc for why it's separate.
+//
+// NB: Besides `folly_private_value_only_awaitable_t`, this is identical to
+// `ValueOrErrorOrStopped`, but the way the `CommutativeWrapperAwaitable`
+// template is set up, it's a lot of code to parameterize it.
+//
+// IMPORTANT: this will never return "stopped" since `BasePromise` sets up
+// `OperationCancelled` to unwind directly to the parent.
+template <typename T>
+class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]
+ValueOrErrorImpl : public CommutativeWrapperAwaitable<ValueOrErrorImpl, T> {
+ public:
+  using CommutativeWrapperAwaitable<ValueOrErrorImpl, T>::
+      CommutativeWrapperAwaitable;
+
+  template <
+      typename Self,
+      std::enable_if_t<
+          std::is_same_v<remove_cvref_t<Self>, ValueOrErrorImpl>,
+          int> = 0,
+      typename T2 = like_t<Self, T>,
+      std::enable_if_t<is_awaitable_v<T2>, int> = 0>
+  friend ResultAwaiter<T2> operator co_await(Self&& self) {
+    return ResultAwaiter<T2>{static_cast<Self&&>(self).inner_};
+  }
+
+  // Future: Cannot mark `folly_private_value_only_awaitable_t` since it
+  // completes with "stopped" which may throw in contexts like `blocking_wait`.
+  // Once the `awaitable_completions_v` refactor is complete, this should
+  // remove the "error" completion from the underlying awaitable.
+};
+
+// Similarly to NothrowAwaitable, this deliberately omits `co_await` and relies
+// on `BasePromise::await_transform`.  The goal is to prevent its use in
+// contexts that cannot provide nothrow-propagation semantics for stopped
+// coros.  Gating on `BasePromise` provides a tightly controlled allowlist of
+// where this can be awaited, and how.
+//
+// For a specific example, consider `blocking_wait(value_or_error(...))` --
+// this should either not compile, or throw on `OperationCancelled`.  The
+// latter "sometimes throwing" semantics would be a surprising footgun, so we
+// instead require the obvious contract of `value_or_error_or_stopped()` here.
+template <typename T>
+class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]
+ValueOrError : public CommutativeWrapperAwaitable<ValueOrError, T> {
+ public:
+  using CommutativeWrapperAwaitable<ValueOrError, T>::
+      CommutativeWrapperAwaitable;
+
+ protected:
+  template <typename>
+  friend class BasePromise;
+  ValueOrErrorImpl<T> toValueOrErrorImpl() && {
+    return ValueOrErrorImpl<T>{folly::ext::must_use_immediately_unsafe_mover(
+        std::move(this->inner_))()};
+  }
+
+  // Future: See `ValueOrErrorImpl` regarding
+  // `folly_private_value_only_awaitable_t` and `awaitable_completions_v`.
+};
+
+} // namespace detail
+
+// BEFORE CHANGING: If you need an `Awaitable&&` overload, you must bifurcate
+// these APIs on `must_use_immediately_v`, see `co_awaitTry` for an example.
+
+/// When awaited, returns a `result<T>` in a value or an error state, but never
+/// `has_stopped()` (aka `OperationCancelled`).  If `awaitable` reports as
+/// "stopped", the awaiting coroutine will be promptly torn down, **without
+/// throwing**, and its parent will in turn receive a "stopped" completion.
+///
+/// BE CAREFUL -- This is `co_nothrow` semantics, but **only** for
+/// cancellation.  -- if your coro requires async cleanup, such as awaiting an
+/// async scope or other background work, then you **must** use a safe async
+/// RAII pattern, such `async_closure` if applicable (or `co_scope_exit`, but
+/// beware of its lifetime risks).
+template <typename Awaitable>
+detail::ValueOrError<Awaitable> value_or_error(
+    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {
+  return detail::ValueOrError<Awaitable>{
+      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))()};
+}
+
+/// When awaited, returns `result<T>` just like `value_or_error()`, but also
+/// captures "stopped" completions, so you **must** test for `has_stopped()`.
+template <typename Awaitable>
+detail::ValueOrErrorOrStopped<Awaitable> value_or_error_or_stopped(
+    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {
+  return detail::ValueOrErrorOrStopped<Awaitable>{
+      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))()};
+}
+
+} // namespace folly::coro
+
+#endif
diff --git a/folly/folly/coro/ValueOrFatal.h b/folly/folly/coro/ValueOrFatal.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/coro/ValueOrFatal.h
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Unit.h>
+#include <folly/coro/TaskWrapper.h>
+#include <folly/coro/ViaIfAsync.h>
+#include <folly/lang/Assume.h>
+#include <folly/result/result.h>
+#include <folly/result/try.h>
+#include <folly/result/value_only_result.h>
+
+#if FOLLY_HAS_COROUTINES
+
+/// `value_or_fatal<Task<T>, Policy>` wraps a task to guarantee value-only
+/// completion.  Use this only with APIs that require it -- like `co_cleanup()`
+/// async RAII, or async scopes.  If your code compiles without it, skip it!
+///
+/// You must specify a policy at least for "stopped" -- from C++26 onward,
+/// cancellation is not an error: https://wg21.link/p1677
+///
+///   Policy                            | Stopped | Error
+///   ----------------------------------|---------|------
+///   on_stopped_void                   | void    | fatal
+///   on_stopped<V>                     | V       | fatal
+///   on_stopped_and_error<V>           | V       | V
+///   on_stopped_and_error<V, W>        | V       | W
+///   on_stopped_and_error<will_fatal>  | fatal   | fatal
+///
+/// ## Why not just `noexcept`?
+///
+/// A `noexcept` coroutine only fatals on errors during frame construction --
+/// awaiting it can still throw.  `value_or_fatal<>` makes the await not throw.
+///
+/// ## This is probably not for you
+///
+///   - Not related to `co_nothrow`, which efficiently propagates exceptions to
+///     the parent, without letting the awaiting task handle them.
+///   - Not a general "avoid exceptions" tool -- the wrapper adds build cost,
+///     frame allocation can still throw, and `std::terminate` offramps can
+///     pessimize vs normal exception propagation.
+///
+/// ## Custom policies
+///
+///   struct my_policy_t {
+///     static int value_only_default(non_value_result&& nv) noexcept {
+///       LOG(ERROR) << (nv.has_stopped() ? "stopped" : "error");
+///       return 0;
+///     }
+///   };
+///   inline constexpr my_policy_t my_policy{};
+///   value_or_fatal<Task<int>, my_policy> foo() { ... }
+
+namespace folly::coro {
+
+// Tag for "terminate on this completion"
+struct will_fatal_t {};
+inline constexpr will_fatal_t will_fatal{};
+
+/// Primary policy struct: specify behavior for stopped (cancellation) and
+/// error. Use via the variable templates below, not directly.
+template <auto Stopped, auto Error>
+class on_stopped_and_error_t {
+ private:
+  template <auto V, typename RetT>
+  static constexpr RetT value_or_terminate(non_value_result&& nv) noexcept {
+    if constexpr (std::is_same_v<decltype(V), will_fatal_t>) {
+      nv.throw_exception(); // Better error than `std::terminate()`
+      folly::assume_unreachable();
+    } else {
+      return V;
+    }
+  }
+
+ public:
+  template <
+      typename RetT = conditional_t<
+          std::is_same_v<decltype(Stopped), will_fatal_t>,
+          decltype(Error),
+          decltype(Stopped)>>
+  static constexpr RetT value_only_default(non_value_result&& nv) noexcept {
+    return nv.has_stopped()
+        ? value_or_terminate<Stopped, RetT>(std::move(nv))
+        : value_or_terminate<Error, RetT>(std::move(nv));
+  }
+};
+
+template <auto Stopped, auto Error = Stopped>
+inline constexpr auto on_stopped_and_error =
+    on_stopped_and_error_t<Stopped, Error>{};
+
+/// Convenience: stopped returns V, error terminates (fatal).
+template <auto V>
+inline constexpr auto on_stopped = on_stopped_and_error<V, will_fatal>;
+
+/// Convenience for void tasks: stopped returns unit, error terminates.
+inline constexpr auto on_stopped_void = on_stopped<unit>;
+
+namespace detail {
+
+// Detect if a type is a TaskWithExecutor-like type (has an attached executor).
+template <typename T>
+inline constexpr bool is_task_with_executor_v = requires {
+  typename T::folly_private_task_without_executor_t;
+};
+
+template <typename Awaitable, auto Policy>
+class ValueOrFatalAwaiter : public ValueOnlyAwaiterBase<Awaitable> {
+ private:
+  using Base = ValueOnlyAwaiterBase<Awaitable>;
+  using RetT = decltype(FOLLY_DECLVAL(typename Base::Awaiter).await_resume());
+
+ public:
+  explicit ValueOrFatalAwaiter(Awaitable&& awaitable)
+      : Base(static_cast<Awaitable&&>(awaitable)) {}
+
+  // Returns `value_only_result<T>` for composition with `await_resume_result()`
+  // protocol. Implicitly converts to `result<T>` for callers expecting that.
+  value_only_result<RetT> await_resume_result() noexcept {
+    // Get result from awaiter using the best available protocol
+    auto res = [&]() -> result<RetT> {
+      if constexpr (is_awaiter_result<typename Base::Awaiter>) {
+        // Catch missed optimization: `awaiter_` returning `value_only_result`
+        // would needlessly convert `value_only_result` to `result`, then back.
+        static_assert(
+            !is_instantiation_of_v<
+                value_only_result,
+                std::remove_cvref_t<
+                    decltype(this->awaiter_.await_resume_result())>>);
+        return this->awaiter_.await_resume_result();
+      } else if constexpr (is_awaiter_try<typename Base::Awaiter>) {
+        return try_to_result(this->awaiter_.await_resume_try());
+      } else {
+        try {
+          if constexpr (std::is_void_v<RetT>) {
+            this->awaiter_.await_resume();
+            return {};
+          } else {
+            return this->awaiter_.await_resume();
+          }
+        } catch (...) { // Policy may handle both "stopped" and "error" below
+          return non_value_result::from_current_exception();
+        }
+      }
+    }();
+
+    // Handle result uniformly
+    if (res.has_value()) {
+      if constexpr (std::is_void_v<RetT>) {
+        return {};
+      } else {
+        return std::move(res).value_or_throw();
+      }
+    } else { // Apply policy to non-value result
+      auto nv = std::move(res).non_value();
+      using PolicyRetT = decltype(Policy.value_only_default(std::move(nv)));
+      if constexpr (std::is_same_v<PolicyRetT, will_fatal_t>) {
+        // Policy terminates (will_fatal), won't return
+        Policy.value_only_default(std::move(nv));
+        folly::assume_unreachable();
+      } else if constexpr (std::is_void_v<RetT>) {
+        Policy.value_only_default(std::move(nv));
+        return {};
+      } else {
+        return Policy.value_only_default(std::move(nv));
+      }
+    }
+  }
+
+  RetT await_resume() noexcept { return await_resume_result().value_only(); }
+
+  // IMPORTANT: There is deliberately NO `await_resume_try()` here. This causes
+  // `co_awaitTry(value_or_fatal(...))` to INTENTIONALLY fail at compile time:
+  //   - `value_or_fatal` guarantees value-only completion
+  //   - `Try`'s error state would be useless (never populated)
+  //   - We want new code to adopt `result`, not `Try`
+  void await_resume_try(auto&&...) = delete;
+};
+
+template <typename, auto>
+class ValueOrFatalAwaitable;
+
+template <auto Policy>
+struct ValueOrFatalAwaitableWithPolicy {
+  template <typename T>
+  using apply = ValueOrFatalAwaitable<T, Policy>;
+};
+
+/// `value_or_fatal<>` / `ValueOrFatalAwaitable<>` compose properly with other
+/// coro- and awaitable-wrappers.  But, not all combinations make sense -- see
+/// the test, and/or extend it if needed.  For example, the outer wrapper is
+/// useless in `ValueOrFatalAwaitable<...>(co_awaitTry(...))`, since exceptions
+/// would already have been routed into a `Try`.
+template <typename T, auto Policy>
+class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] ValueOrFatalAwaitable
+    : public CommutativeWrapperAwaitable<
+          ValueOrFatalAwaitableWithPolicy<Policy>::template apply,
+          T> {
+ public:
+  using CommutativeWrapperAwaitable<
+      ValueOrFatalAwaitableWithPolicy<Policy>::template apply,
+      T>::CommutativeWrapperAwaitable;
+
+  template <typename T2 = T, std::enable_if_t<is_awaitable_v<T2>, int> = 0>
+  ValueOrFatalAwaiter<T, Policy> operator co_await() && {
+    return ValueOrFatalAwaiter<T, Policy>{std::move(this->inner_)};
+  }
+
+  using folly_private_value_only_awaitable_t = std::true_type;
+};
+
+} // namespace detail
+
+#if FOLLY_HAS_IMMOVABLE_COROUTINES
+
+template <typename Inner, auto Policy>
+class value_or_fatal;
+
+namespace detail {
+
+// Configuration for wrapping TaskWithExecutor-like types
+template <typename Inner, auto Policy>
+struct value_or_fatal_with_executor_cfg {
+  using InnerTaskWithExecutorT = Inner;
+  using WrapperTaskT = value_or_fatal<
+      typename Inner::folly_private_task_without_executor_t,
+      Policy>;
+  template <typename Awaitable>
+  static inline auto wrapAwaitable(Awaitable&& awaitable) noexcept {
+    static_assert(!value_only_awaitable_v<Awaitable>); // Don't double-wrap
+    return detail::ValueOrFatalAwaitable<Awaitable, Policy>{
+        folly::ext::must_use_immediately_unsafe_mover(
+            static_cast<Awaitable&&>(awaitable))()};
+  }
+};
+
+template <typename Inner, auto Policy>
+using value_or_fatal_with_executor_base = TaskWithExecutorWrapperCrtp<
+    value_or_fatal<Inner, Policy>,
+    value_or_fatal_with_executor_cfg<Inner, Policy>>;
+
+template <typename... BaseArgs>
+class value_or_fatal_task_promise_wrapper final
+    : public TaskPromiseWrapper<BaseArgs...> {};
+
+// Configuration for wrapping Task-like types
+template <typename Inner, auto Policy>
+struct value_or_fatal_cfg {
+  using ValueT = semi_await_result_t<Inner>;
+  using InnerTaskT = Inner;
+  using TaskWithExecutorT = value_or_fatal<
+      decltype(co_withExecutor(
+          FOLLY_DECLVAL(Executor::KeepAlive<>), FOLLY_DECLVAL(Inner))),
+      Policy>;
+  using PromiseT = value_or_fatal_task_promise_wrapper<
+      ValueT,
+      value_or_fatal<Inner, Policy>,
+      typename folly::coro::coroutine_traits<Inner>::promise_type>;
+  template <typename Awaitable>
+  static inline auto wrapAwaitable(Awaitable&& awaitable) noexcept {
+    static_assert(!value_only_awaitable_v<Awaitable>); // Don't double-wrap
+    return detail::ValueOrFatalAwaitable<Awaitable, Policy>{
+        static_cast<Awaitable&&>(awaitable)};
+  }
+};
+
+template <typename Inner, auto Policy>
+using value_or_fatal_base = TaskWrapperCrtp<
+    value_or_fatal<Inner, Policy>,
+    value_or_fatal_cfg<Inner, Policy>>;
+
+// Selects base class based on whether Inner has an attached executor
+template <typename Inner, auto Policy>
+using value_or_fatal_auto_base = conditional_t<
+    is_task_with_executor_v<Inner>,
+    value_or_fatal_with_executor_base<Inner, Policy>,
+    value_or_fatal_base<Inner, Policy>>;
+
+// CAUTION: `value_or_fatal_rewrapper` gives you the power to wrap and unwrap
+// `value_or_fatal`, so you must be extremely careful to preserve behavior:
+//   - The unwrapped task must be rewrapped before awaiting.
+//   - You must not wrap any other task.
+
+template <typename>
+struct value_or_fatal_rewrapper {
+  static inline constexpr bool value_or_fatal_wrapped = false;
+  static auto wrap_with(auto fn) { return fn(); }
+};
+
+template <typename Inner, auto Policy>
+struct value_or_fatal_rewrapper<value_or_fatal<Inner, Policy>> {
+  static inline constexpr bool value_or_fatal_wrapped = true;
+  static Inner unwrapTask(value_or_fatal<Inner, Policy>&& t) {
+    return std::move(t).unwrapTask();
+  }
+  static auto wrap_with(auto fn) {
+    return value_or_fatal<decltype(fn()), Policy>{fn()};
+  }
+};
+
+} // namespace detail
+
+/// `value_or_fatal<Task<T>, Policy>` wraps a task to guarantee value-only
+/// completion.
+template <typename Inner, auto Policy>
+class FOLLY_CORO_TASK_ATTRS value_or_fatal final
+    : public detail::value_or_fatal_auto_base<Inner, Policy> {
+ protected:
+  using detail::value_or_fatal_auto_base<Inner, Policy>::
+      value_or_fatal_auto_base;
+
+  template <typename>
+  friend struct detail::value_or_fatal_rewrapper;
+
+ public:
+  using folly_private_value_only_awaitable_t = std::true_type;
+};
+
+#endif // FOLLY_HAS_IMMOVABLE_COROUTINES
+
+} // namespace folly::coro
+
+#endif // FOLLY_HAS_COROUTINES
diff --git a/folly/folly/coro/ViaIfAsync.h b/folly/folly/coro/ViaIfAsync.h
--- a/folly/folly/coro/ViaIfAsync.h
+++ b/folly/folly/coro/ViaIfAsync.h
@@ -20,7 +20,6 @@
 
 #include <folly/Executor.h>
 #include <folly/Traits.h>
-#include <folly/coro/AwaitImmediately.h>
 #include <folly/coro/Coroutine.h>
 #include <folly/coro/Traits.h>
 #include <folly/coro/WithAsyncStack.h>
@@ -28,6 +27,7 @@
 #include <folly/coro/detail/Malloc.h>
 #include <folly/io/async/Request.h>
 #include <folly/lang/CustomizationPoint.h>
+#include <folly/lang/MustUseImmediately.h>
 #include <folly/lang/SafeAlias-fwd.h>
 #include <folly/tracing/AsyncStack.h>
 
@@ -113,7 +113,7 @@
  public:
   class promise_type final
       : public ViaCoroutinePromiseBase,
-        public ExtendedCoroutinePromise {
+        public ExtendedCoroutinePromiseCrtp<promise_type> {
     struct FinalAwaiter {
       bool await_ready() noexcept { return false; }
 
@@ -154,14 +154,14 @@
 
     folly::AsyncStackFrame& getLeafFrame() noexcept { return leafFrame_; }
 
-    std::pair<ExtendedCoroutineHandle, AsyncStackFrame*> getErrorHandle(
-        exception_wrapper& ex) final {
-      auto [handle, frame] = continuation_.getErrorHandle(ex);
-      setContinuation(handle);
+    static std::optional<ExtendedCoroutineHandle::ErrorHandle>
+    getErrorHandleImpl(promise_type& me, exception_wrapper& ex) {
+      auto [handle, frame] = me.continuation_.getErrorHandle(ex);
+      me.setContinuation(handle);
       if (frame && IsStackAware) {
-        leafFrame_.setParentFrame(*frame);
+        me.leafFrame_.setParentFrame(*frame);
       }
-      return {coroutine_handle<promise_type>::from_promise(*this), nullptr};
+      return std::nullopt;
     }
   };
 
@@ -240,8 +240,9 @@
   explicit StackAwareViaIfAsyncAwaiter(
       folly::Executor::KeepAlive<> executor, Awaitable&& awaitable)
       : viaCoroutine_(CoroutineType::create(std::move(executor))),
-        awaitable_(folly::coro::co_withAsyncStack(
-            static_cast<Awaitable&&>(awaitable))),
+        awaitable_(
+            folly::coro::co_withAsyncStack(
+                static_cast<Awaitable&&>(awaitable))),
         awaiter_(
             get_awaiter(static_cast<WithAsyncStackAwaitable&&>(awaitable_))) {}
 
@@ -517,7 +518,7 @@
     std::enable_if_t<
         is_awaitable_v<Awaitable> && !HasViaIfAsyncMethod<Awaitable>::value,
         int> = 0,
-    std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
+    std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
 auto co_viaIfAsync(folly::Executor::KeepAlive<> executor, Awaitable&& awaitable)
     -> ViaIfAsyncAwaitable<Awaitable> {
   return ViaIfAsyncAwaitable<Awaitable>{
@@ -528,7 +529,7 @@
     std::enable_if_t<
         is_awaitable_v<Awaitable> && !HasViaIfAsyncMethod<Awaitable>::value,
         int> = 0,
-    std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
+    std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
 auto co_viaIfAsync(folly::Executor::KeepAlive<> executor, Awaitable awaitable)
     -> ViaIfAsyncAwaitable<Awaitable> {
   return ViaIfAsyncAwaitable<Awaitable>{
@@ -538,7 +539,7 @@
 struct ViaIfAsyncFunction {
   template <
       typename Awaitable,
-      std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
+      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
   auto operator()(folly::Executor::KeepAlive<> executor, Awaitable&& awaitable)
       const noexcept(noexcept(co_viaIfAsync(
           std::move(executor), static_cast<Awaitable&&>(awaitable))))
@@ -549,17 +550,19 @@
   }
   template <
       typename Awaitable,
-      std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
+      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
   auto operator()(folly::Executor::KeepAlive<> executor, Awaitable awaitable)
       const noexcept(noexcept(co_viaIfAsync(
           std::move(executor),
-          mustAwaitImmediatelyUnsafeMover(std::move(awaitable))())))
+          folly::ext::must_use_immediately_unsafe_mover(
+              std::move(awaitable))())))
           -> decltype(co_viaIfAsync(
               std::move(executor),
-              mustAwaitImmediatelyUnsafeMover(std::move(awaitable))())) {
+              folly::ext::must_use_immediately_unsafe_mover(
+                  std::move(awaitable))())) {
     return co_viaIfAsync(
         std::move(executor),
-        mustAwaitImmediatelyUnsafeMover(std::move(awaitable))());
+        folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))());
   }
 };
 
@@ -598,35 +601,73 @@
 
 namespace detail {
 
+/// Mixin for awaiters that forward to an inner awaiter with value-only
+/// semantics. Provides `await_ready()` and `await_suspend()` with static
+/// asserts that both are noexcept. Derived classes implement `await_resume()`.
+///
+/// GUIDANCE FOR NEW AWAITABLES: Design `await_ready` and `await_suspend` to
+/// be noexcept.  If they must check for invariant violations:
+///   - If feasible, push invariant failures into `await_resume()`.
+///   - Otherwise, debug-fatal on violations, and throw an error with a clearly
+///     "do not catch me" naming in opt.
+template <typename Awaitable>
+class ValueOnlyAwaiterBase {
+ public:
+  using Awaiter = awaiter_type_t<Awaitable>;
+
+ protected:
+  Awaiter awaiter_;
+
+  explicit ValueOnlyAwaiterBase(Awaitable&& awaitable)
+      : awaiter_(get_awaiter(static_cast<Awaitable&&>(awaitable))) {}
+
+ public:
+  auto await_ready() noexcept -> decltype(awaiter_.await_ready()) {
+    static_assert(
+        noexcept(FOLLY_DECLVAL(Awaiter&).await_ready()),
+        "value-only await requires noexcept await_ready(); see class doc");
+    return awaiter_.await_ready();
+  }
+
+  template <typename Promise>
+  auto await_suspend(coroutine_handle<Promise> coro) noexcept
+      -> decltype(awaiter_.await_suspend(coro)) {
+    static_assert(
+        noexcept(awaiter_.await_suspend(coro)),
+        "value-only await requires noexcept await_suspend(); see class doc");
+    return awaiter_.await_suspend(coro);
+  }
+};
+
 template <typename T>
-using noexcept_awaitable_of_ = typename T::folly_private_noexcept_awaitable_t;
+using value_only_awaitable_of_ =
+    typename T::folly_private_value_only_awaitable_t;
 
 template <typename Void, typename T>
-struct noexcept_awaitable_ {
-  static_assert(require_sizeof<T>, "`noexcept_awaitable_t` on incomplete type");
+struct value_only_awaitable_ {
+  static_assert(
+      require_sizeof<T>, "`value_only_awaitable_t` on incomplete type");
   using type = std::false_type;
 };
 
 template <>
-struct noexcept_awaitable_<void, void> {
+struct value_only_awaitable_<void, void> {
   using type = std::false_type;
 };
 
 template <typename T>
-struct noexcept_awaitable_<void_t<noexcept_awaitable_of_<T>>, T> {
-  using type = noexcept_awaitable_of_<T>;
+struct value_only_awaitable_<void_t<value_only_awaitable_of_<T>>, T> {
+  using type = value_only_awaitable_of_<T>;
 };
 
 } // namespace detail
 
-// This trait is in `ViaIfAsync.h` so that we don't have include `Noexcept.h`
-// If there's ever a use-case that doesn't depend on `ViaIfAsync.h`, this can
-// be moved up to `Traits.h`
+/// True for awaitables that only complete with value (not stopped or error)
 template <typename T>
-using noexcept_awaitable_t =
-    typename detail::noexcept_awaitable_<void, T>::type;
+using value_only_awaitable_t =
+    typename detail::value_only_awaitable_<void, T>::type;
 template <typename T>
-inline constexpr bool noexcept_awaitable_v = noexcept_awaitable_t<T>::value;
+inline constexpr bool value_only_awaitable_v = value_only_awaitable_t<T>::value;
 
 namespace detail {
 
@@ -641,35 +682,15 @@
 constexpr bool is_awaitable_try = is_awaiter_try<awaiter_type_t<Awaitable>>;
 
 template <typename Awaitable>
-class TryAwaiter {
-  static_assert(is_awaitable_try<Awaitable&&>);
-
-  using Awaiter = awaiter_type_t<Awaitable>;
-
+class TryAwaiter : public ValueOnlyAwaiterBase<Awaitable> {
  public:
-  explicit TryAwaiter(Awaitable&& awaiter)
-      : awaiter_(get_awaiter(static_cast<Awaitable&&>(awaiter))) {}
-
-  auto await_ready() noexcept(noexcept(std::declval<Awaiter&>().await_ready()))
-      -> decltype(std::declval<Awaiter&>().await_ready()) {
-    return awaiter_.await_ready();
-  }
-
-  template <typename Promise>
-  auto await_suspend(coroutine_handle<Promise> coro) noexcept(
-      noexcept(std::declval<Awaiter&>().await_suspend(coro)))
-      -> decltype(std::declval<Awaiter&>().await_suspend(coro)) {
-    return awaiter_.await_suspend(coro);
-  }
+  static_assert(is_awaitable_try<Awaitable&&>);
 
-  auto await_resume() noexcept(
-      noexcept(std::declval<Awaiter&>().await_resume_try()))
-      -> decltype(std::declval<Awaiter&>().await_resume_try()) {
-    return awaiter_.await_resume_try();
-  }
+  explicit TryAwaiter(Awaitable&& awaitable)
+      : ValueOnlyAwaiterBase<Awaitable>(static_cast<Awaitable&&>(awaitable)) {}
 
- private:
-  Awaiter awaiter_;
+  auto await_resume()
+      FOLLY_DETAIL_FORWARD_BODY(this->awaiter_.await_resume_try())
 };
 
 /**
@@ -682,51 +703,60 @@
  public:
   template <
       typename T2,
-      std::enable_if_t<!must_await_immediately_v<T2>, int> = 0>
+      std::enable_if_t<!folly::ext::must_use_immediately_v<T2>, int> = 0>
   explicit CommutativeWrapperAwaitable(T2&& awaitable) noexcept(
       std::is_nothrow_constructible_v<T, T2>)
       : inner_(static_cast<T2&&>(awaitable)) {}
   template <
       typename T2,
-      std::enable_if_t<must_await_immediately_v<T2>, int> = 0>
-  explicit CommutativeWrapperAwaitable(T2 awaitable)
-      // `mustAwaitImmediatelyUnsafeMover` has more `noexcept` assertions.
-      noexcept(noexcept(T{FOLLY_DECLVAL(T2)}))
-      : inner_(mustAwaitImmediatelyUnsafeMover(std::move(awaitable))()) {}
+      std::enable_if_t<folly::ext::must_use_immediately_v<T2>, int> = 0>
+  explicit CommutativeWrapperAwaitable(T2 awaitable) noexcept(noexcept(T{
+      FOLLY_DECLVAL(T2)}))
+      : inner_(
+            folly::ext::must_use_immediately_unsafe_mover(
+                std::move(awaitable))()) {}
 
   template <typename Factory>
   explicit CommutativeWrapperAwaitable(std::in_place_t, Factory&& factory)
       : inner_(factory()) {}
 
+  // Two overloads for the CancellationToken to avoid unnecessary copies
+  // (atomic refcount costs).
+  //
+  // NB: If we merged the overloads into a single template, overload resolution
+  // rules would consider it ambiguous wrt the default implementation in
+  // `WithCancellation.h`.
   template <
       typename T2 = T,
-      typename Result = decltype(folly::coro::co_withCancellation(
-          FOLLY_DECLVAL(const folly::CancellationToken&), FOLLY_DECLVAL(T2&&)))>
+      // "WART:" in `WithCancellation.h` explains the remove-reference
+      typename Result =
+          std::remove_reference_t<decltype(folly::coro::co_withCancellation(
+              FOLLY_DECLVAL(const folly::CancellationToken&),
+              FOLLY_DECLVAL(T2)))>>
   friend Derived<Result> co_withCancellation(
-      const folly::CancellationToken& cancelToken, Derived<T>&& awaitable) {
+      const folly::CancellationToken& cancelToken, Derived<T> awaitable) {
     return Derived<Result>{
-        std::in_place, [&]() -> decltype(auto) {
+        std::in_place, [&]() {
           return folly::coro::co_withCancellation(
-              cancelToken, static_cast<T&&>(awaitable.inner_));
+              cancelToken,
+              folly::ext::must_use_immediately_unsafe_mover(
+                  std::move(awaitable.inner_))());
         }};
   }
-  // This overload exists to avoid unnecessarily copying `cancelToken`, which
-  // has atomic refcount costs.
-  //  - Taking it by-value would force unnecessary token copies for underlying
-  //    awaitables that ignore the token.
-  //  - If we merged the overloads into a single template, overload resolution
-  //    rules would consider it ambiguous wrt the default implementation in
-  //    `WithCancellation.h`.
   template <
       typename T2 = T,
-      typename Result = decltype(folly::coro::co_withCancellation(
-          FOLLY_DECLVAL(folly::CancellationToken&&), FOLLY_DECLVAL(T2&&)))>
+      // "WART:" in `WithCancellation.h` explains the remove-reference
+      typename Result =
+          std::remove_reference_t<decltype(folly::coro::co_withCancellation(
+              FOLLY_DECLVAL(folly::CancellationToken&&), FOLLY_DECLVAL(T2)))>>
   friend Derived<Result> co_withCancellation(
-      folly::CancellationToken&& cancelToken, Derived<T>&& awaitable) {
+      folly::CancellationToken&& cancelToken, Derived<T> awaitable) {
     return Derived<Result>{
-        std::in_place, [&]() -> decltype(auto) {
+        std::in_place, [&]() {
           return folly::coro::co_withCancellation(
-              std::move(cancelToken), static_cast<T&&>(awaitable.inner_));
+              std::move(cancelToken),
+              folly::ext::must_use_immediately_unsafe_mover(
+                  std::move(awaitable.inner_))());
         }};
   }
 
@@ -744,25 +774,7 @@
         }};
   }
 
-  template <
-      typename T2 = T,
-      std::enable_if_t<!must_await_immediately_v<T2>, int> = 0,
-      typename Result = semi_await_awaitable_t<T2>>
-  friend Derived<Result> co_viaIfAsync(
-      folly::Executor::KeepAlive<> executor,
-      Derived<T>&& awaitable) //
-      noexcept(noexcept(folly::coro::co_viaIfAsync(
-          FOLLY_DECLVAL(folly::Executor::KeepAlive<>), FOLLY_DECLVAL(T2)))) {
-    return Derived<Result>{
-        std::in_place, [&]() -> decltype(auto) {
-          return folly::coro::co_viaIfAsync(
-              std::move(executor), static_cast<T&&>(awaitable.inner_));
-        }};
-  }
-  template <
-      typename T2 = T,
-      std::enable_if_t<must_await_immediately_v<T2>, int> = 0,
-      typename Result = semi_await_awaitable_t<T2>>
+  template <typename T2 = T, typename Result = semi_await_awaitable_t<T2>>
   friend Derived<Result> co_viaIfAsync(
       folly::Executor::KeepAlive<> executor,
       Derived<T> awaitable) //
@@ -772,36 +784,66 @@
         std::in_place, [&]() {
           return folly::coro::co_viaIfAsync(
               std::move(executor),
-              mustAwaitImmediatelyUnsafeMover(std::move(awaitable.inner_))());
+              folly::ext::must_use_immediately_unsafe_mover(
+                  std::move(awaitable.inner_))());
         }};
   }
 
-  template <
-      typename T2 = T,
-      typename = decltype(FOLLY_DECLVAL(T2&&).getUnsafeMover(
-          FOLLY_DECLVAL(ForMustAwaitImmediately)))>
-  auto getUnsafeMover(ForMustAwaitImmediately p) && noexcept {
-    // See "A note on object slicing" above `mustAwaitImmediatelyUnsafeMover`
-    static_assert(sizeof(Derived<T>) == sizeof(T));
-    static_assert( // More `noexcept` tests in `MustAwaitImmediatelyUnsafeMover`
-        noexcept(std::move(inner_).getUnsafeMover(p)));
-    return MustAwaitImmediatelyUnsafeMover{
-        (Derived<T>*)nullptr, std::move(inner_).getUnsafeMover(p)};
-  }
-
   // IMPORTANT: If a commutative wrapper changes safety, immediate- or
   // noexcept-awaitability, it must remember to override these:
-  using folly_private_must_await_immediately_t = must_await_immediately_t<T>;
-  using folly_private_noexcept_awaitable_t = noexcept_awaitable_t<T>;
-  using folly_private_safe_alias_t = safe_alias_of<T>;
+  using folly_must_use_immediately_t = ext::must_use_immediately_t<T>;
+  using folly_private_value_only_awaitable_t = value_only_awaitable_t<T>;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t = safe_alias_of<T, Default>;
 
  protected:
   T inner_;
+
+ private:
+  template <typename U>
+  using my_curried_mover = folly::ext::curried_unsafe_mover_t<
+      U,
+      decltype(folly::ext::must_use_immediately_unsafe_mover(
+          FOLLY_DECLVAL(T)))>;
+
+ public:
+  template <
+      typename Me, // not a forwarding ref, see SFINAE
+      typename T2 = T,
+      std::enable_if_t<
+          // This check guards against misuse (+ fails on lvalue refs)
+          // See `wrap_must_use_immediately_t::unsafe_mover` for more context
+          std::is_base_of_v<CommutativeWrapperAwaitable, Me> &&
+              // Without this check we might instantiate this for things like
+              // `TryAwaitable<coro::Future<...>&&>`, erroring with:
+              //   "cannot form a pointer-to-member to member of reference type"
+              folly::ext::must_use_immediately_v<T2>,
+          int> = 0>
+  static my_curried_mover<Me> unsafe_mover(
+      folly::ext::must_use_immediately_private_t, Me&& me) noexcept {
+    return folly::ext::curried_unsafe_mover_from_bases_and_members<
+        CommutativeWrapperAwaitable>(
+        folly::tag</*no bases*/>,
+        folly::vtag<&CommutativeWrapperAwaitable::inner_>,
+        static_cast<Me&&>(me));
+  }
+  template <
+      typename DerivedFromMe,
+      // Matches the SFINAE logic in our `unsafe_mover`
+      std::enable_if_t<
+          std::is_base_of_v<CommutativeWrapperAwaitable, DerivedFromMe>,
+          int> = 0>
+  explicit CommutativeWrapperAwaitable(
+      folly::ext::curried_unsafe_mover_private_t,
+      my_curried_mover<DerivedFromMe>&& mover)
+      // `must_use_immediately_unsafe_mover` has more `noexcept` assertions
+      noexcept(noexcept(T{std::move(mover.template get<0>())()}))
+      : inner_{std::move(mover.template get<0>())()} {}
 };
 
 template <typename T>
-class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] TryAwaitable
-    : public CommutativeWrapperAwaitable<TryAwaitable, T> {
+class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]
+TryAwaitable : public CommutativeWrapperAwaitable<TryAwaitable, T> {
  public:
   using CommutativeWrapperAwaitable<TryAwaitable, T>::
       CommutativeWrapperAwaitable;
@@ -813,18 +855,18 @@
           int> = 0,
       typename T2 = like_t<Self, T>,
       std::enable_if_t<is_awaitable_v<T2>, int> = 0>
-  friend TryAwaiter<T2> operator co_await(Self && self) {
+  friend TryAwaiter<T2> operator co_await(Self&& self) {
     return TryAwaiter<T2>{static_cast<Self&&>(self).inner_};
   }
 
-  using folly_private_noexcept_awaitable_t = std::true_type;
+  using folly_private_value_only_awaitable_t = std::true_type;
 };
 
 } // namespace detail
 
 template <
     typename Awaitable,
-    std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
+    std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
 detail::TryAwaitable<remove_cvref_t<Awaitable>> co_awaitTry(
     [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable&& awaitable) {
   return detail::TryAwaitable<remove_cvref_t<Awaitable>>{
@@ -832,47 +874,16 @@
 }
 template <
     typename Awaitable,
-    std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
+    std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
 detail::TryAwaitable<Awaitable> co_awaitTry(
     [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {
   return detail::TryAwaitable<Awaitable>{
-      mustAwaitImmediatelyUnsafeMover(std::move(awaitable))()};
+      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))()};
 }
 
 template <typename T>
 using semi_await_try_result_t = await_result_t<semi_await_awaitable_t<
     decltype(folly::coro::co_awaitTry(FOLLY_DECLVAL(T)))>>;
-
-namespace detail {
-
-template <typename T>
-class [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] NothrowAwaitable
-    : public CommutativeWrapperAwaitable<NothrowAwaitable, T> {
- public:
-  using CommutativeWrapperAwaitable<NothrowAwaitable, T>::
-      CommutativeWrapperAwaitable;
-
-  T&& unwrap() { return std::move(this->inner_); }
-};
-
-} // namespace detail
-
-template <
-    typename Awaitable,
-    std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
-detail::NothrowAwaitable<remove_cvref_t<Awaitable>> co_nothrow(
-    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable&& awaitable) {
-  return detail::NothrowAwaitable<remove_cvref_t<Awaitable>>{
-      static_cast<Awaitable&&>(awaitable)};
-}
-template <
-    typename Awaitable,
-    std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
-detail::NothrowAwaitable<remove_cvref_t<Awaitable>> co_nothrow(
-    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {
-  return detail::NothrowAwaitable<remove_cvref_t<Awaitable>>{
-      mustAwaitImmediatelyUnsafeMover(std::move(awaitable))()};
-}
 
 } // namespace folly::coro
 
diff --git a/folly/folly/coro/WithAsyncStack.h b/folly/folly/coro/WithAsyncStack.h
--- a/folly/folly/coro/WithAsyncStack.h
+++ b/folly/folly/coro/WithAsyncStack.h
@@ -116,6 +116,10 @@
 class WithAsyncStackAwaiter {
   using Awaiter = awaiter_type_t<Awaitable>;
 
+  static constexpr bool kSuspendIsNoexcept =
+      noexcept(FOLLY_DECLVAL(Awaiter&).await_suspend(FOLLY_DECLVAL(
+          coroutine_handle<WithAsyncStackCoroutine::promise_type>)));
+
  public:
   explicit WithAsyncStackAwaiter(Awaitable&& awaitable)
       : awaiter_(get_awaiter(static_cast<Awaitable&&>(awaitable))),
@@ -129,7 +133,8 @@
   // needs to be no-inline as return address is being captured for async stack
   // tracing
   template <typename Promise>
-  FOLLY_NOINLINE auto await_suspend(coroutine_handle<Promise> h) {
+  FOLLY_NOINLINE auto await_suspend(coroutine_handle<Promise> h) noexcept(
+      kSuspendIsNoexcept) {
     AsyncStackFrame& callerFrame = h.promise().getAsyncFrame();
     AsyncStackRoot* stackRoot = callerFrame.getStackRoot();
     assert(stackRoot != nullptr);
@@ -143,7 +148,7 @@
     using await_suspend_result_t =
         decltype(awaiter_.await_suspend(wrapperHandle));
 
-    try {
+    auto doSuspend = [&]() {
       if constexpr (std::is_same_v<await_suspend_result_t, bool>) {
         if (!awaiter_.await_suspend(wrapperHandle)) {
           folly::activateAsyncStackFrame(*stackRoot, callerFrame);
@@ -154,10 +159,19 @@
       } else {
         return awaiter_.await_suspend(wrapperHandle);
       }
-    } catch (...) {
-      folly::activateAsyncStackFrame(*stackRoot, callerFrame);
-      folly::deactivateSuspendedLeaf(coroWrapper_.getLeafFrame());
-      throw;
+    };
+
+    if constexpr (kSuspendIsNoexcept) {
+      return doSuspend();
+    } else {
+      // Restore async stack state on exception, then rethrow.
+      try {
+        return doSuspend();
+      } catch (...) {
+        folly::activateAsyncStackFrame(*stackRoot, callerFrame);
+        folly::deactivateSuspendedLeaf(coroWrapper_.getLeafFrame());
+        throw;
+      }
     }
   }
 
diff --git a/folly/folly/coro/WithCancellation.h b/folly/folly/coro/WithCancellation.h
--- a/folly/folly/coro/WithCancellation.h
+++ b/folly/folly/coro/WithCancellation.h
@@ -17,9 +17,9 @@
 #pragma once
 
 #include <folly/CancellationToken.h>
-#include <folly/coro/AwaitImmediately.h>
 #include <folly/coro/Coroutine.h>
 #include <folly/lang/CustomizationPoint.h>
+#include <folly/lang/MustUseImmediately.h>
 
 #if FOLLY_HAS_COROUTINES
 
@@ -41,23 +41,27 @@
 /// Types must opt-in to hooking cancellation by customising this function.
 template <
     typename Awaitable,
-    std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
+    std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
+// WART: The `Awaitable&&` overload must return `Awaitable&&` to support
+// `Baton`s, which is immovable and awaitable only by lvalue.  We could remove
+// this, and simplify other code, by migrating `Baton`s to emit an explicit
+// must-use-immediately awaitable instead of awaiting them by lvalue.
 Awaitable&& co_withCancellation(
     const folly::CancellationToken&, Awaitable&& awaitable) noexcept {
   return static_cast<Awaitable&&>(awaitable);
 }
 template <
     typename Awaitable,
-    std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
+    std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
 Awaitable co_withCancellation(
     const folly::CancellationToken&, Awaitable awaitable) noexcept {
-  return mustAwaitImmediatelyUnsafeMover(std::move(awaitable))();
+  return folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))();
 }
 
 struct WithCancellationFunction {
   template <
       typename Awaitable,
-      std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
+      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
   auto operator()(
       const folly::CancellationToken& cancelToken, Awaitable&& awaitable) const
       noexcept(noexcept(co_withCancellation(
@@ -69,21 +73,24 @@
   }
   template <
       typename Awaitable,
-      std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
+      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
   auto operator()(
       const folly::CancellationToken& cancelToken, Awaitable awaitable) const
       noexcept(noexcept(co_withCancellation(
           cancelToken,
-          mustAwaitImmediatelyUnsafeMover(std::move(awaitable))())))
+          folly::ext::must_use_immediately_unsafe_mover(
+              std::move(awaitable))())))
           -> decltype(co_withCancellation(
               cancelToken,
-              mustAwaitImmediatelyUnsafeMover(std::move(awaitable))())) {
+              folly::ext::must_use_immediately_unsafe_mover(
+                  std::move(awaitable))())) {
     return co_withCancellation(
-        cancelToken, mustAwaitImmediatelyUnsafeMover(std::move(awaitable))());
+        cancelToken,
+        folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))());
   }
   template <
       typename Awaitable,
-      std::enable_if_t<!must_await_immediately_v<Awaitable>, int> = 0>
+      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
   auto operator()(folly::CancellationToken&& cancelToken, Awaitable&& awaitable)
       const noexcept(noexcept(co_withCancellation(
           std::move(cancelToken), static_cast<Awaitable&&>(awaitable))))
@@ -94,17 +101,19 @@
   }
   template <
       typename Awaitable,
-      std::enable_if_t<must_await_immediately_v<Awaitable>, int> = 0>
+      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>
   auto operator()(folly::CancellationToken&& cancelToken, Awaitable awaitable)
       const noexcept(noexcept(co_withCancellation(
           std::move(cancelToken),
-          mustAwaitImmediatelyUnsafeMover(std::move(awaitable))())))
+          folly::ext::must_use_immediately_unsafe_mover(
+              std::move(awaitable))())))
           -> decltype(co_withCancellation(
               std::move(cancelToken),
-              mustAwaitImmediatelyUnsafeMover(std::move(awaitable))())) {
+              folly::ext::must_use_immediately_unsafe_mover(
+                  std::move(awaitable))())) {
     return co_withCancellation(
         std::move(cancelToken),
-        mustAwaitImmediatelyUnsafeMover(std::move(awaitable))());
+        folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))());
   }
 };
 } // namespace adl
diff --git a/folly/folly/coro/detail/PickTaskWrapper.h b/folly/folly/coro/detail/PickTaskWrapper.h
--- a/folly/folly/coro/detail/PickTaskWrapper.h
+++ b/folly/folly/coro/detail/PickTaskWrapper.h
@@ -21,14 +21,15 @@
 
 /// For functions-of-coros, like `timeout()` or `collectAll()`, we want the
 /// outer coro to be able to pass through these attributes of the inner coro:
-///  - `must_await_immediately_v`
-///  - `noexcept_awaitable_v`
-///  - `safe_alias_of_v`
+///  - `folly::ext::must_use_immediately_v`
+///  - `value_only_awaitable_v`
+///  - `strict_safe_alias_of_v` / `lenient_safe_alias_of_v`
 ///
 /// Variation along these dimensions is currently implemented as a zoo of coro
-/// templates and wrappers -- `Task` aka `UnsafeMovableTask`, `NowTask`,
-/// `SafeTask`, `AsNoexcept<InnerTask>`.  The type function `PickTaskWrapper`
-/// provides common logic for picking a task type with the given attributes.
+/// templates and wrappers -- `Task` aka `UnsafeMovableTask`, `now_task`,
+/// `safe_task`, `value_or_fatal<InnerTask>`.  The type function
+/// `pick_task_wrapper` provides common logic for picking a task type with the
+/// given attributes.
 
 #if FOLLY_HAS_COROUTINES
 
@@ -40,17 +41,17 @@
 class TaskWithExecutor;
 
 template <safe_alias, typename>
-class SafeTask;
+class safe_task;
 template <safe_alias, typename>
-class SafeTaskWithExecutor;
+class safe_task_with_executor;
 
 template <typename T>
-class NowTask;
+class now_task;
 template <typename T>
-class NowTaskWithExecutor;
+class now_task_with_executor;
 
 template <typename, auto>
-class AsNoexcept;
+class value_or_fatal;
 
 namespace detail {
 
@@ -59,13 +60,13 @@
   using apply = T;
 };
 
-template <safe_alias, bool /*must await immediately (now)*/>
-struct PickTaskWrapperImpl;
+template <safe_alias, bool /*must use immediately (now)*/>
+struct pick_task_wrapper_impl;
 
 #if FOLLY_HAS_IMMOVABLE_COROUTINES
 
 template <>
-struct PickTaskWrapperImpl<safe_alias::unsafe, /*await now*/ false> {
+struct pick_task_wrapper_impl<safe_alias::unsafe, /*await now*/ false> {
   template <typename T>
   using Task = Task<T>;
   template <typename T>
@@ -73,37 +74,37 @@
 };
 
 template <>
-struct PickTaskWrapperImpl<safe_alias::unsafe, /*await now*/ true> {
+struct pick_task_wrapper_impl<safe_alias::unsafe, /*await now*/ true> {
   template <typename T>
-  using Task = NowTask<T>;
+  using Task = now_task<T>;
   template <typename T>
-  using TaskWithExecutor = NowTaskWithExecutor<T>;
+  using TaskWithExecutor = now_task_with_executor<T>;
 };
 
-// These `SafeTask` types are immovable, so "await now" doesn't matter.
+// These `safe_task` types are immovable, so "await now" doesn't matter.
 template <safe_alias Safety, bool AwaitNow>
   requires(Safety < safe_alias::closure_min_arg_safety)
-struct PickTaskWrapperImpl<Safety, AwaitNow> {
+struct pick_task_wrapper_impl<Safety, AwaitNow> {
   template <typename T>
-  using Task = SafeTask<Safety, T>;
+  using Task = safe_task<Safety, T>;
   template <typename T>
-  using TaskWithExecutor = SafeTaskWithExecutor<Safety, T>;
+  using TaskWithExecutor = safe_task_with_executor<Safety, T>;
 };
 
 template <safe_alias Safety>
   requires(Safety >= safe_alias::closure_min_arg_safety)
-// Future: There is no principled reason we can't have must-await-immediately
-// `SafeTask`s with these higher safety levels, but supporting that cleanly
+// Future: There is no principled reason we can't have must-use-immediately
+// `safe_task`s with these higher safety levels, but supporting that cleanly
 // would require reorganizing the `folly/coro` task-wrapper implementations. Two
 // possible approaches are:
-//  - `NowTask<T> = AwaitNow<Task<T>>`
-//  - Roll up `NowTask` and `SafeTask` into something like `BasicTask<T, Cfg>`,
+//  - `now_task<T> = await_now<Task<T>>`
+//  - Roll up `now_task` and `safe_task` into `basic_task<T, Cfg>` or similar,
 //    where `Cfg` captures both safety & immediate-awaitability.
-struct PickTaskWrapperImpl<Safety, /*await now*/ false> {
+struct pick_task_wrapper_impl<Safety, /*await now*/ false> {
   template <typename T>
-  using Task = SafeTask<Safety, T>;
+  using Task = safe_task<Safety, T>;
   template <typename T>
-  using TaskWithExecutor = SafeTaskWithExecutor<Safety, T>;
+  using TaskWithExecutor = safe_task_with_executor<Safety, T>;
 };
 
 #else // no FOLLY_HAS_IMMOVABLE_COROUTINES
@@ -111,7 +112,7 @@
 // This fallback is required because `coro::Future<SafeType>` is safe and is
 // available on earlier build systems.  We have no choice but to emit `Task`.
 template <safe_alias Safety>
-struct PickTaskWrapperImpl<Safety, /*await now*/ false> {
+struct pick_task_wrapper_impl<Safety, /*await now*/ false> {
   template <typename T>
   using Task = Task<T>;
   template <typename T>
@@ -120,30 +121,32 @@
 
 #endif // FOLLY_HAS_IMMOVABLE_COROUTINES
 
-// Pass this as `AddWrapperMetaFn` to `PickTaskWrapper` to add `AsNoexcept`.
+// Pass this as `AddWrapperMetaFn` to `pick_task_wrapper` to add
+// `value_or_fatal`.
 template <auto CancelCfg>
-struct AsNoexceptWithCancelCfg {
+struct value_or_fatal_with_cancel_cfg {
   template <typename T>
-  using apply = AsNoexcept<T, CancelCfg>;
+  using apply = value_or_fatal<T, CancelCfg>;
 };
 
 template <
     typename T,
     safe_alias Safety,
-    bool MustAwaitImmediately,
+    bool MustUseImmediately,
     typename AddWrapperMetaFn = identity_metafunction>
-using PickTaskWrapper = typename AddWrapperMetaFn::template apply<
-    typename PickTaskWrapperImpl<Safety, MustAwaitImmediately>::template Task<
+using pick_task_wrapper = typename AddWrapperMetaFn::template apply<
+    typename pick_task_wrapper_impl<Safety, MustUseImmediately>::template Task<
         T>>;
 
 template <
     typename T,
     safe_alias Safety,
-    bool MustAwaitImmediately,
+    bool MustUseImmediately,
     typename AddWrapperMetaFn = identity_metafunction>
-using PickTaskWithExecutorWrapper = typename AddWrapperMetaFn::template apply<
-    typename PickTaskWrapperImpl<Safety, MustAwaitImmediately>::
-        template TaskWithExecutor<T>>;
+using pick_task_with_executor_wrapper =
+    typename AddWrapperMetaFn::template apply<typename pick_task_wrapper_impl<
+        Safety,
+        MustUseImmediately>::template TaskWithExecutor<T>>;
 
 } // namespace detail
 } // namespace folly::coro
diff --git a/folly/folly/coro/safe/AsyncClosure.h b/folly/folly/coro/safe/AsyncClosure.h
--- a/folly/folly/coro/safe/AsyncClosure.h
+++ b/folly/folly/coro/safe/AsyncClosure.h
@@ -25,22 +25,23 @@
 /// Learn more about `coro/safe` tools by browsing `docs/`.  Start with
 /// `README.md` and `AsyncClosure.md`.  Here's a tl;dr for `AsyncClosure.h`.
 ///
-/// Use `async_closure()` / `async_now_closure()` only when `NowTask` is not
+/// Use `async_closure()` / `async_now_closure()` only when `now_task` is not
 /// enough.  For your effort, you get (1) guaranteed, exception-safe async
 /// RAII, and (2) the resulting coro is an automatically-measured movable
-/// `SafeTask`, which improves lifetime safety (`LifetimeSafetyBenefits.md`).
+/// `safe_task`, which improves lifetime safety (`LifetimeSafetyBenefits.md`).
 ///
-/// Control flow matches a regular `NowTask` lazy-start coro:
+/// Control flow matches a regular `now_task` lazy-start coro:
 ///   - All "argument-binding" and "coro creation" work is eager.
 ///   - Your inner task & subsequent cleanup can only run when awaited.
 ///
-/// `async_closure(bound_args{...}, taskFn)` wraps an outer task around yours,
+/// `async_closure(bind::args{...}, taskFn)` wraps an outer task around yours,
 /// unless elided via an automatic optimization.  The outer task owns special
 /// "capture" args passed to the closure, ensuring they outlive the inner task.
 ///
 /// Lifecycle contract:
-///   - `bound_args{}` evaluate left-to-right (L2R) due to `{}`.
-///   - Construction of `capture_in_place` / `make_in_place` args is also L2R.
+///   - `bind::args{}` evaluate left-to-right (L2R) due to `{}`.
+///   - Construction of `bind::capture_in_place` / `bind::in_place` args is also
+///   L2R.
 ///   - When args are passed to the inner coro, copy/move order is unspecified.
 ///   - Upon awaiting the inner coro, `setParentCancelToken()` is called on the
 ///     capture args in L2R order.
@@ -48,7 +49,7 @@
 ///   - When the outer coro exits, captures are destroyed in R2L order.
 ///
 /// The `co_cleanup()` and `setParentCancelToken()` protocols support capture
-/// types like `SafeAsyncScope` and `BackgroundTask`, which give the user
+/// types like `safe_async_scope` and `BackgroundTask`, which give the user
 /// guaranteed, exception-safe async cleanup. Before building custom async
 /// RAII, carefully read `CoCleanupAsyncRAII.md`.
 ///
@@ -56,8 +57,8 @@
 /// which implements a similar "async RAII" contract for object scopes.
 ///
 /// The difference between `async_closure()` and `async_now_closure()` is that
-/// the former measures argument & inner coro safety, and makes a `SafeTask`,
-/// while the latter has no safety checks, and makes a `NowTask`.  Both make it
+/// the former measures argument & inner coro safety, and makes a `safe_task`,
+/// while the latter has no safety checks, and makes a `now_task`.  Both make it
 /// easy to write lifetime-safe code.
 
 struct async_closure_config {
@@ -93,13 +94,11 @@
 
 namespace detail {
 template <bool ForceOuterCoro, bool EmitNowTask>
-// OK to take `bound_args` by-ref since the porcelain functions take it by-value
+// OK to take `bind::args` by-ref since the porcelain functions take it by-value
 auto async_closure_impl(auto&& bargs, auto&& make_inner_coro) {
   constexpr detail::async_closure_bindings_cfg Cfg{
       .force_outer_coro = ForceOuterCoro,
-      // `NowTask`s closures have no safety controls, and thus -- like
-      // "shared cleanup" closures -- don't get to upgrade `capture` refs.
-      .force_shared_cleanup = EmitNowTask,
+      .emit_now_task = EmitNowTask,
       .is_invoke_member = is_instantiation_of_v<
           invoke_member_wrapper_fn,
           std::remove_reference_t<decltype(make_inner_coro)>>};
@@ -110,8 +109,8 @@
 }
 } // namespace detail
 
-// Makes a `SafeTask` whose safety is determined by the supplied arguments.
-// `SafeTask` requires that (1) the inner coroutine must not take arguments
+// Makes a `safe_task` whose safety is determined by the supplied arguments.
+// `safe_task` requires that (1) the inner coroutine must not take arguments
 // by-reference, and (2) must have a `maybe_value`-safe return type.
 //
 // Caveat: When `make_inner_coro` is a coroutine wrapper, that part is
@@ -120,9 +119,9 @@
 // Coro creation, argument storage, and in-place construction are also
 // synchronous, as is the movement of the args into the task coroutine.
 //
-// The first argument should be `bound_args{...}`.  For single-argument
-// closures, you can omit the `bound_args` if you're passing `as_capture()`,
-// `capture_in_place<>()`, or another `like_bound_args` item.
+// The first argument should be `bind::args{...}`.  For single-argument
+// closures, you can omit the `bind::args` if you're passing `bind::capture()`,
+// `bind::capture_in_place<>()`, or another `bind::ext::like_args` item.
 //
 // Async RAII: Awaiting the task ensures `co_cleanup(async_closure_private_t)`
 // is awaited for each of the `capture` arguments that defines it.
@@ -142,18 +141,18 @@
 }
 
 // Like `async_closure` -- same argument binding semantics, same `co_cleanup`
-// async RAII, and cancellation support, but returns a non-movable `NowTask`
+// async RAII, and cancellation support, but returns a non-movable `now_task`
 // without the lifetime safety enforcement:
-//   - `make_inner_coro` may return a `NowTask`, plain `Task`, or `SafeTask`.
+//   - `make_inner_coro` may return a `now_task`, plain `Task`, or `safe_task`.
 //   - It can take arguments by ref, you can pass raw pointers, etc.
 //   - There are no checks on the `co_return` type.
 //
 // Requiring the task to be immediately awaited prevents a lot of common
 // lifetime bugs.  If you cannot immediately await the task, then you should
-// review `LifetimSafetyBenefits.md` and use the `SafeTask`-enabled
-// `async_closure()`, which is movable and schedulable on `SafeAsyncScope`.
+// review `LifetimSafetyBenefits.md` and use the `safe_task`-enabled
+// `async_closure()`, which is movable and schedulable on `safe_async_scope`.
 //
-// BEWARE: Returning `NowTask` doesn't prevent egregious bugs like returning
+// BEWARE: Returning `now_task` doesn't prevent egregious bugs like returning
 // a pointer to a local.  Instead, make sure to configure your compiler to
 // error on simple, non-async lifetime bugs (e.g. `-Wdangling -Werror`).
 template <async_closure_config Cfg = async_closure_config{}>
diff --git a/folly/folly/coro/safe/BindCaptures.h b/folly/folly/coro/safe/BindCaptures.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/coro/safe/BindCaptures.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/lang/bind/Bind.h>
+
+#if FOLLY_HAS_IMMOVABLE_COROUTINES
+
+///
+/// Please read the user- and developer-facing docs in `Capture.md`.
+///
+/// Callers typically only include `BindCaptures.h`, while callees need to
+/// include `Captures.h`.  Both are provided by `AsyncClosure.h`.
+///
+/// NOTE:
+///   - The binding helpers in this file are all in `folly::bind::` for
+///     uniformity with the other related verbs -- `bind::args`,
+///     `bind::in_place` `bind::constant`, etc.
+///   - The `bind_to_storage_policy` specialization for this is only used
+///     in one file, so it's inlined in `BindAsyncClosure.h`.
+///
+
+namespace folly::bind {
+
+namespace detail {
+
+// Any binding with this key is meant to be owned by the async closure
+enum class capture_kind {
+  plain = 0,
+  // Syntax sugar: Passing `bind::capture_indirect()` with a pointer-like (e.g.
+  // `unique_ptr<T>`), this emits a `capture_indirect<>`, giving access to
+  // the underlying `T` with just one dereference `*` / `->`, instead of 2.
+  indirect,
+};
+
+struct capture_bind_info_t : folly::bind::ext::bind_info_t {
+  capture_kind captureKind_;
+
+  constexpr explicit capture_bind_info_t(
+      // Using a constraint prevents object slicing
+      std::same_as<folly::bind::ext::bind_info_t> auto bi,
+      capture_kind ap)
+      : folly::bind::ext::bind_info_t(std::move(bi)), captureKind_(ap) {}
+};
+
+template <capture_kind Kind, typename UpdateBI = std::identity>
+struct capture_bind_info {
+  // Using `auto` prevents object slicing
+  constexpr auto operator()(auto bi) {
+    return capture_bind_info_t{UpdateBI{}(std::move(bi)), Kind};
+  }
+};
+
+} // namespace detail
+
+///
+/// `bind::capture()` and `bind::capture_indirect()` work much like other
+/// `folly::bind` modifiers.  However, since they're primarily intended
+/// for `async_closure` arguments, you will practically only use them:
+///   - alone, for non-`co_cleanup` arguments;
+///   - with `bind::in_place()` or `bind::in_place_with()`, for `co_cleanup`
+///     arguments;
+///   - with `constant()`, for either.
+///
+/// `capture_in_place<T>()` is short for `bind::capture(bind::in_place<T>())`.
+///
+/// See `Captures.md` and `folly/lang/Bindings.md`.
+///
+
+template <typename... Ts>
+struct capture
+    : ::folly::bind::ext::merge_update_args<
+          detail::capture_bind_info<detail::capture_kind::plain>,
+          Ts...> {
+  using ::folly::bind::ext::merge_update_args<
+      detail::capture_bind_info<detail::capture_kind::plain>,
+      Ts...>::merge_update_args;
+};
+template <typename... Ts>
+capture(Ts&&...) -> capture<folly::bind::ext::deduce_args_t<Ts>...>;
+
+template <typename... Ts>
+struct capture_indirect
+    : ::folly::bind::ext::merge_update_args<
+          detail::capture_bind_info<detail::capture_kind::indirect>,
+          Ts...> {
+  using ::folly::bind::ext::merge_update_args<
+      detail::capture_bind_info<detail::capture_kind::indirect>,
+      Ts...>::merge_update_args;
+};
+template <typename... Ts>
+capture_indirect(Ts&&...)
+    -> capture_indirect<folly::bind::ext::deduce_args_t<Ts>...>;
+
+// Sugar for `capture{const_ref{...}}`
+template <typename... Ts>
+struct capture_const_ref
+    : ::folly::bind::ext::merge_update_args<
+          detail::capture_bind_info<
+              detail::capture_kind::plain,
+              ::folly::bind::detail::const_ref_bind_info>,
+          Ts...> {
+  using ::folly::bind::ext::merge_update_args<
+      detail::capture_bind_info<
+          detail::capture_kind::plain,
+          ::folly::bind::detail::const_ref_bind_info>,
+      Ts...>::merge_update_args;
+};
+template <typename... Ts>
+capture_const_ref(Ts&&...)
+    -> capture_const_ref<folly::bind::ext::deduce_args_t<Ts>...>;
+// Sugar for `capture{mut_ref{...}}`
+template <typename... Ts>
+struct capture_mut_ref
+    : ::folly::bind::ext::merge_update_args<
+          detail::capture_bind_info<
+              detail::capture_kind::plain,
+              ::folly::bind::detail::mut_ref_bind_info>,
+          Ts...> {
+  using ::folly::bind::ext::merge_update_args<
+      detail::capture_bind_info<
+          detail::capture_kind::plain,
+          ::folly::bind::detail::mut_ref_bind_info>,
+      Ts...>::merge_update_args;
+};
+template <typename... Ts>
+capture_mut_ref(Ts&&...)
+    -> capture_mut_ref<folly::bind::ext::deduce_args_t<Ts>...>;
+
+// Sugar for `capture{in_place<T>(...)}`
+template <typename T>
+auto capture_in_place(auto&&... as [[clang::lifetimebound]]) {
+  return capture(bind::in_place<T>(static_cast<decltype(as)>(as)...));
+}
+// Sugar for `capture{in_place_with(fn, ...)}`
+auto capture_in_place_with(
+    auto make_fn, auto&&... as [[clang::lifetimebound]]) {
+  return capture(
+      bind::in_place_with(
+          std::move(make_fn), static_cast<decltype(as)>(as)...));
+}
+
+} // namespace folly::bind
+
+#endif
diff --git a/folly/folly/coro/safe/Captures.h b/folly/folly/coro/safe/Captures.h
--- a/folly/folly/coro/safe/Captures.h
+++ b/folly/folly/coro/safe/Captures.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <folly/Portability.h>
 #include <folly/Traits.h>
 #include <folly/Utility.h>
 #include <folly/coro/safe/AsyncClosure-fwd.h>
@@ -23,13 +24,13 @@
 // `#undef`ed at end-of-file not to leak this macro.
 #include <folly/coro/safe/detail/DefineMovableDeepConstLrefCopyable.h>
 #include <folly/detail/tuple.h>
-#include <folly/lang/Assume.h>
-#include <folly/lang/Bindings.h>
-#include <folly/lang/named/Bindings.h>
 
 ///
 /// Please read the user- and developer-facing docs in `Capture.md`.
 ///
+/// Callers typically only include `BindCaptures.h`, while callees need to
+/// include `Captures.h`.  Both are provided by `AsyncClosure.h`.
+///
 
 #if FOLLY_HAS_IMMOVABLE_COROUTINES
 
@@ -40,13 +41,10 @@
 
 namespace folly::coro {
 
-// Re-export `bound_args` since it's required to use async closures & objects.
-using ::folly::bindings::bound_args;
-
 class AsyncObjectTag;
 
 template <safe_alias, typename>
-class SafeTask;
+class safe_task;
 
 namespace detail {
 
@@ -96,127 +94,12 @@
     !std::is_copy_assignable_v<T> && !std::is_move_constructible_v<T> &&
     !std::is_move_assignable_v<T> && !std::swappable<T>;
 
-// Any binding with this key is meant to be owned by the async closure
-enum class capture_kind {
-  plain = 0,
-  // Syntax sugar: Passing `as_capture_indirect()` with a pointer-like (e.g.
-  // `unique_ptr<T>`), this emits a `capture_indirect<>`, giving access to
-  // the underlying `T` with just one dereference `*` / `->`, instead of 2.
-  indirect,
-};
-
-struct capture_bind_info_t : folly::bindings::ext::bind_info_t {
-  capture_kind captureKind_;
-
-  constexpr explicit capture_bind_info_t(
-      // Using a constraint prevents object slicing
-      std::same_as<folly::bindings::ext::bind_info_t> auto bi,
-      capture_kind ap)
-      : folly::bindings::ext::bind_info_t(std::move(bi)), captureKind_(ap) {}
-};
-
-template <capture_kind Kind, typename UpdateBI = std::identity>
-struct as_capture_bind_info {
-  // Using `auto` prevents object slicing
-  constexpr auto operator()(auto bi) {
-    return capture_bind_info_t{UpdateBI{}(std::move(bi)), Kind};
-  }
-};
-
 template <typename, template <typename> class, typename>
 class capture_crtp_base;
 
 } // namespace detail
 
-///
-/// `as_capture()` and `as_capture_indirect()` work much like other
-/// `folly::bindings` modifiers.  However, since they're primarily intended
-/// for `async_closure` arguments, you will practically only use them:
-///   - alone, for non-`co_cleanup` arguments;
-///   - with `make_in_place()` or `make_in_place_with()`, for `co_cleanup`
-///     arguments;
-///   - with `constant()`, for either.
-///
-/// `capture_in_place<T>()` is short for `as_capture(make_in_place<T>())`.
-///
-/// See `Captures.md` and `folly/lang/Bindings.md`.
-///
-
-template <typename... Ts>
-struct as_capture
-    : ::folly::bindings::ext::merge_update_bound_args<
-          detail::as_capture_bind_info<detail::capture_kind::plain>,
-          Ts...> {
-  using ::folly::bindings::ext::merge_update_bound_args<
-      detail::as_capture_bind_info<detail::capture_kind::plain>,
-      Ts...>::merge_update_bound_args;
-};
-template <typename... Ts>
-as_capture(Ts&&...)
-    -> as_capture<folly::bindings::ext::deduce_bound_args_t<Ts>...>;
-
-template <typename... Ts>
-struct as_capture_indirect
-    : ::folly::bindings::ext::merge_update_bound_args<
-          detail::as_capture_bind_info<detail::capture_kind::indirect>,
-          Ts...> {
-  using ::folly::bindings::ext::merge_update_bound_args<
-      detail::as_capture_bind_info<detail::capture_kind::indirect>,
-      Ts...>::merge_update_bound_args;
-};
-template <typename... Ts>
-as_capture_indirect(Ts&&...)
-    -> as_capture_indirect<folly::bindings::ext::deduce_bound_args_t<Ts>...>;
-
-// Sugar for `as_capture{const_ref{...}}`
-template <typename... Ts>
-struct capture_const_ref
-    : ::folly::bindings::ext::merge_update_bound_args<
-          detail::as_capture_bind_info<
-              detail::capture_kind::plain,
-              ::folly::bindings::detail::const_ref_bind_info>,
-          Ts...> {
-  using ::folly::bindings::ext::merge_update_bound_args<
-      detail::as_capture_bind_info<
-          detail::capture_kind::plain,
-          ::folly::bindings::detail::const_ref_bind_info>,
-      Ts...>::merge_update_bound_args;
-};
-template <typename... Ts>
-capture_const_ref(Ts&&...)
-    -> capture_const_ref<folly::bindings::ext::deduce_bound_args_t<Ts>...>;
-// Sugar for `as_capture{mut_ref{...}}`
-template <typename... Ts>
-struct capture_mut_ref
-    : ::folly::bindings::ext::merge_update_bound_args<
-          detail::as_capture_bind_info<
-              detail::capture_kind::plain,
-              ::folly::bindings::detail::mut_ref_bind_info>,
-          Ts...> {
-  using ::folly::bindings::ext::merge_update_bound_args<
-      detail::as_capture_bind_info<
-          detail::capture_kind::plain,
-          ::folly::bindings::detail::mut_ref_bind_info>,
-      Ts...>::merge_update_bound_args;
-};
-template <typename... Ts>
-capture_mut_ref(Ts&&...)
-    -> capture_mut_ref<folly::bindings::ext::deduce_bound_args_t<Ts>...>;
-
-// Sugar for `as_capture{make_in_place<T>(...)}`
 template <typename T>
-auto capture_in_place(auto&&... as [[clang::lifetimebound]]) {
-  return as_capture(
-      ::folly::bindings::make_in_place<T>(static_cast<decltype(as)>(as)...));
-}
-// Sugar for `as_capture{make_in_place_with(fn, ...)}`
-auto capture_in_place_with(
-    auto make_fn, auto&&... as [[clang::lifetimebound]]) {
-  return as_capture(::folly::bindings::make_in_place_with(
-      std::move(make_fn), static_cast<decltype(as)>(as)...));
-}
-
-template <typename T>
   requires(!detail::has_async_closure_co_cleanup<T>)
 class capture;
 template <typename T>
@@ -272,17 +155,12 @@
 
 namespace detail {
 
-class capture_private_t {
- protected:
-  friend struct CapturesTest;
-  template <typename, template <typename> class, typename>
-  friend class capture_crtp_base;
-  template <typename, auto, size_t>
-  friend class capture_binding_helper;
-  template <auto>
-  friend auto bind_captures_to_closure(auto&&, auto);
-  friend constexpr capture_private_t coro_safe_detail_bindings_test_private();
-  friend class ::folly::coro::AsyncObjectTag;
+// DANGER: Using this passkey makes it easy to break the lifetime-safety
+// guarantees of `folly/coro/safe`, so before adding a new callsite, get
+// familiar with the lifetime-safety docs, and get a careful review.  This used
+// to have a private-with-friends constructor, but the friend list grew
+// unmanageably large as the number of lifetime-safe utilities increased.
+struct capture_private_t {
   explicit capture_private_t() = default;
 };
 
@@ -329,7 +207,7 @@
     return fn();
   }
 
-  // Object intended for use with `capture`  (like `SafeAsyncScope`) may
+  // Object intended for use with `capture`  (like `safe_async_scope`) may
   // provide overloads of the helper function `capture_proxy` to provide
   // proxy types for `capture` operators `*` and `->`.
   //
@@ -345,7 +223,7 @@
   //     `co_cleanup_capture<...AsyncScope...>` that was originally NOT
   //     restricted -- so, "restricted" is a property of the reference, not
   //     of the underlying scope object.
-  //   - Therefore, the public API of `SafeAsyncScope` must sit in a
+  //   - Therefore, the public API of `safe_async_scope` must sit in a
   //     "reference" object that knows if it's restricted, not in the storage
   //     object (which does not).
   //   - It would break encapsulation to put `AsyncScope`-specific logic like
@@ -372,7 +250,13 @@
     } else if constexpr (Kind == ext::capture_proxy_kind::lval_ref) {
       return lref; // Unproxied l-value reference
     } else if constexpr (Kind == ext::capture_proxy_kind::rval_ref) {
-      return std::move(lref); // Unproxied r-value reference
+      // Implement regular forwarding-ref semantics:
+      //   (V&)&& -> V&, (V)&& -> V&&, (V&&)&& -> V&&
+      if constexpr (std::is_lvalue_reference_v<T>) {
+        return lref;
+      } else {
+        return std::move(lref);
+      }
     } else if constexpr (
         Kind == ext::capture_proxy_kind::lval_ptr ||
         Kind == ext::capture_proxy_kind::rval_ptr) {
@@ -517,7 +401,7 @@
   // of your closure.  That deliberately has stricter single-use semantics
   // than `V&&` in vanilla C++ -- for example, without single-use, an rref
   // could be used to move out a value that is still referenced in
-  // SafeAsyncScope task.  Having the explicit && -> & conversion permits
+  // safe_async_scope task.  Having the explicit && -> & conversion permits
   // the child change its mind about moving out the value.
   //
   // Future ideas & implementation notes:
@@ -701,7 +585,7 @@
 // dereference operations into one for better UX.  There is no need for a
 // `capture_heap_indirect_storage`, since this "indirect" syntax sugar only
 // applies to pointer types, which are always cheaply movable, and thus
-// don't benefit from `make_in_place`.
+// don't benefit from `bind::in_place`.
 //
 // Similarly, no support for `co_cleanup()` captures since those generally
 // aren't pointer-like, and won't suffer from double-dereferences.
@@ -728,18 +612,18 @@
         std::move(*this).capture_storage<Derived, RefArgT, T>::operator*());
   }
   [[nodiscard]] constexpr decltype(auto) operator->() & noexcept {
-    return (capture_storage<Derived, RefArgT, T>::operator->())->operator->();
+    return (capture_storage<Derived, RefArgT, T>::operator->()) -> operator->();
   }
   [[nodiscard]] constexpr decltype(auto) operator->() const& noexcept {
-    return (capture_storage<Derived, RefArgT, T>::operator->())->operator->();
+    return (capture_storage<Derived, RefArgT, T>::operator->()) -> operator->();
   }
   [[nodiscard]] constexpr decltype(auto) operator->() && noexcept {
     return (std::move(*this).capture_storage<Derived, RefArgT, T>::operator->())
-        ->operator->();
+        -> operator->();
   }
   [[nodiscard]] constexpr decltype(auto) operator->() const&& noexcept {
     return (std::move(*this).capture_storage<Derived, RefArgT, T>::operator->())
-        ->operator->();
+        -> operator->();
   }
 
   // Unlike other captures, `capture_indirect` is nullable since the
@@ -769,6 +653,41 @@
   }
 };
 
+// `capture_safety_impl_v` is separate for `AsyncObject.h` to specialize
+template <typename T, safe_alias Default>
+inline constexpr auto capture_safety_impl_v = safe_alias_of<T, Default>::value;
+
+// ALL "capture" types must have `folly_private_safe_alias_t` markings.
+//
+// `capture` refs are only valid as long as their on-closure storage.  They can
+// be copied/moved, so their `safe_alias` marking is the only thing preventing
+// the use of invalid references.  The docs in `enum class safe_alias` discuss
+// how safety levels are assigned for closure `capture`s.  `async_closure`
+// invokes `to_capture_ref()` to emit refs with the appropriate safety.
+//
+// If the underlying type is `<= shared_cleanup`, that leaks through to
+// all `capture`s containing it.  See e.g. `AsyncObjectPtr`.
+//   * Note: A `shared_cleanup` type `T` gives a closure a way of passing refs
+//     onto parent `safe_async_scope`s (generically: cleanup phases), so
+//     `capture<T>` must never be safer than `T` (unless we're dealing with a
+//     restricted capture ref),
+//
+// Otherwise, the safety measurement of `T` is "outer" to the current
+// closure, and is one of `after_cleanup_ref`, `co_cleanup_safe_ref`, or
+// `maybe_value`.  Those should all behave the same inside the closure,
+// so `MaxRefSafety` is all that matters.
+//   * Note: `capture<V>` is convertible to `capture<V&>` etc, so the ref
+//     version should never be safer.
+template <typename T, safe_alias MaxRefSafety, safe_alias Default>
+struct capture_safety
+    : safe_alias_constant<
+          (capture_safety_impl_v<std::remove_reference_t<T>, Default> <=
+           safe_alias::shared_cleanup)
+              ? std::min(
+                    MaxRefSafety,
+                    capture_safety_impl_v<std::remove_reference_t<T>, Default>)
+              : MaxRefSafety> {};
+
 } // namespace detail
 
 // Please read the file docblock.
@@ -794,6 +713,9 @@
  public:
   FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(capture, T);
   using detail::capture_storage<capture<T>, capture, T>::capture_storage;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t =
+      detail::capture_safety<T, safe_alias::co_cleanup_safe_ref, Default>;
 };
 template <typename T> // may be a value or reference
   requires(!detail::has_async_closure_co_cleanup<T>)
@@ -806,14 +728,17 @@
       after_cleanup_capture<T>,
       after_cleanup_capture,
       T>::capture_storage;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t =
+      detail::capture_safety<T, safe_alias::after_cleanup_ref, Default>;
 };
 
 // The use-case for `capture_heap` is to allow a closure without cleanup
 // args to avoid an inner/outer task split, while still taking
-// `make_in_place` arguments.  This is meant to be an implementation detail
+// `bind::in_place` arguments.  This is meant to be an implementation detail
 // that's almost fully API-compatible with `capture`.  At a future
 // point we *could* remove this:
-//  - Then, any use of `make_in_place` would auto-create an outer task.
+//  - Then, any use of `bind::in_place` would auto-create an outer task.
 //  - Any user code that explicitly specifies `capture_heap` in signatures
 //    would need to be updated to `capture`.
 //  - Any places that rely on moving `capture_heap<V>` would need to migrate
@@ -826,6 +751,9 @@
  public:
   using detail::capture_heap_storage<capture_heap<T>, capture, T>::
       capture_heap_storage;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t =
+      detail::capture_safety<T, safe_alias::co_cleanup_safe_ref, Default>;
 };
 template <typename T>
 class after_cleanup_capture_heap
@@ -838,6 +766,9 @@
       after_cleanup_capture_heap<T>,
       after_cleanup_capture,
       T>::capture_heap_storage;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t =
+      detail::capture_safety<T, safe_alias::after_cleanup_ref, Default>;
 };
 
 // `capture_indirect<SomePtr<T>>` is like `capture<SomePtr<T>>` with syntax
@@ -852,6 +783,9 @@
       capture_indirect<T>,
       capture_indirect,
       T>::capture_indirect_storage;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t =
+      detail::capture_safety<T, safe_alias::co_cleanup_safe_ref, Default>;
 };
 template <typename T>
 class after_cleanup_capture_indirect
@@ -864,6 +798,9 @@
       after_cleanup_capture_indirect<T>,
       after_cleanup_capture_indirect,
       T>::capture_indirect_storage;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t =
+      detail::capture_safety<T, safe_alias::after_cleanup_ref, Default>;
 };
 
 // A closure that takes a cleanup arg is required to mark its directly-owned
@@ -887,6 +824,9 @@
   FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(co_cleanup_capture, T);
   using detail::capture_storage<co_cleanup_capture<T>, co_cleanup_capture, T>::
       capture_storage;
+  template <safe_alias Default>
+  using folly_private_safe_alias_t =
+      detail::capture_safety<T, safe_alias::shared_cleanup, Default>;
 };
 
 // What this accomplishes, in brief -- details in `Captures.md`:
@@ -916,6 +856,12 @@
       restricted_co_cleanup_capture<T>,
       restricted_co_cleanup_capture,
       T>::capture_storage;
+  // FIXME: `capture_safety` will still measure this as `shared_cleanup` due
+  // to `T` being that safety.  So, when implementing restricted refs, we'll
+  // have to add a new case to `capture_safety` to handle this.
+  template <safe_alias Default>
+  using folly_private_safe_alias_t =
+      detail::capture_safety<T, safe_alias::after_cleanup_ref, Default>;
 };
 
 namespace detail {
@@ -940,99 +886,22 @@
 concept is_any_capture_val =
     is_any_capture<T> && !std::is_reference_v<typename T::capture_type>;
 
-// `capture_safety_impl_v` is separate for `AsyncObject.h` to specialize
-template <typename T>
-inline constexpr auto capture_safety_impl_v = safe_alias_of_v<T>;
-// If the underlying type is `<= shared_cleanup`, that leaks through to
-// all `capture`s containing it.  See e.g. `AsyncObjectPtr`.
-//   * Note: A `shared_cleanup` type `T` gives a closure a way of passing refs
-//     onto parent `SafeAsyncScope`s (generically: cleanup phases), so
-//     `capture<T>` must never be safer than `T` (unless we're dealing with a
-//     restricted capture ref),
-//
-// Otherwise, the safety measurement of `T` is "outer" to the current
-// closure, and is one of `after_cleanup_ref`, `co_cleanup_safe_ref`, or
-// `maybe_value`.  Those should all behave the same inside the closure,
-// so `MaxRefSafety` is all that matters.
-//   * Note: `capture<V>` is convertible to `capture<V&>` etc, so the ref
-//     version should never be safer.
-template <typename T, safe_alias MaxRefSafety>
-struct capture_safety
-    : safe_alias_constant<
-          (capture_safety_impl_v<std::remove_reference_t<T>> <=
-           safe_alias::shared_cleanup)
-              ? std::min(
-                    MaxRefSafety,
-                    capture_safety_impl_v<std::remove_reference_t<T>>)
-              : MaxRefSafety> {};
-
 } // namespace detail
 
 } // namespace folly::coro
 
-namespace folly {
-
-// Set `safe_alias` values for all the `capture` types.
-//
-// `capture` refs are only valid as long as their on-closure storage.  They
-// can be copied/moved, so their `safe_alias` marking is the only thing
-// preventing the use of invalid references.  The docs in `enum class
-// safe_alias` discuss how safety levels are assigned for closure
-// `capture`s.  `async_closure` invokes `to_capture_ref()` to emit refs with
-// the appropriate safety.
-
-template <typename T>
-struct safe_alias_of<::folly::coro::capture<T>>
-    : folly::coro::detail::capture_safety<T, safe_alias::co_cleanup_safe_ref> {
-};
-template <typename T>
-struct safe_alias_of<::folly::coro::capture_heap<T>>
-    : folly::coro::detail::capture_safety<T, safe_alias::co_cleanup_safe_ref> {
-};
-template <typename T>
-struct safe_alias_of<::folly::coro::capture_indirect<T>>
-    : folly::coro::detail::capture_safety<T, safe_alias::co_cleanup_safe_ref> {
-};
-
-template <typename T>
-struct safe_alias_of<::folly::coro::after_cleanup_capture<T>>
-    : folly::coro::detail::capture_safety<T, safe_alias::after_cleanup_ref> {};
-template <typename T>
-struct safe_alias_of<::folly::coro::after_cleanup_capture_heap<T>>
-    : folly::coro::detail::capture_safety<T, safe_alias::after_cleanup_ref> {};
-template <typename T>
-struct safe_alias_of<::folly::coro::after_cleanup_capture_indirect<T>>
-    : folly::coro::detail::capture_safety<T, safe_alias::after_cleanup_ref> {};
-
-template <typename T>
-struct safe_alias_of<::folly::coro::co_cleanup_capture<T>>
-    : folly::coro::detail::capture_safety<T, safe_alias::shared_cleanup> {};
-// FIXME: `capture_safety` will still measure this as `shared_cleanup` due
-// to `T` being that safety.  So, when implementing restricted refs, we'll
-// have to add a new case to `capture_safety` to handle this.
-template <typename T>
-struct safe_alias_of<::folly::coro::restricted_co_cleanup_capture<T>>
-    : folly::coro::detail::capture_safety<T, safe_alias::after_cleanup_ref> {};
-
-} // namespace folly
-
-// We extended `folly::bindings` with `capture_kind`, so we must explicitly
-// specialize `binding_policy`.  We reuse the standard rules.  Custom
-// `capture` binding logic is in `async_closure_bindings()`.
-namespace folly::bindings::ext {
-template <auto BI, typename BindingType>
-  requires std::same_as< // Written as a constraint to prevent object slicing
-      decltype(BI),
-      ::folly::coro::detail::capture_bind_info_t>
-class binding_policy<ext::binding_t<BI, BindingType>> {
- private:
-  using standard = binding_policy<ext::binding_t<bind_info_t{BI}, BindingType>>;
-
- public:
-  using storage_type = typename standard::storage_type;
-  using signature_type = typename standard::signature_type;
+// Customize `safe_closure` to store `capture<V>` as `capture<V&>`.
+namespace folly::detail {
+template <typename ST>
+struct safe_closure_arg_storage_helper;
+template <coro::detail::is_any_capture_val ST>
+struct safe_closure_arg_storage_helper<ST> {
+  // We should never move `capture<Val>`s, so `safe_closure` will fail to
+  // implicitly convert from `capture<Val>&&` to `capture<V&>` since the
+  // `&&`-qualified conversions are `explicit` above.
+  using type = coro::capture_ref_conversion_t<ST&>;
 };
-} // namespace folly::bindings::ext
+} // namespace folly::detail
 
 #endif
 
diff --git a/folly/folly/coro/safe/NowTask.h b/folly/folly/coro/safe/NowTask.h
--- a/folly/folly/coro/safe/NowTask.h
+++ b/folly/folly/coro/safe/NowTask.h
@@ -21,20 +21,28 @@
 
 #if FOLLY_HAS_IMMOVABLE_COROUTINES
 
-/// `NowTask<T>` quacks like `Task<T>` but is immovable, and must be
+/// `now_task<T>` quacks like `Task<T>` but is immovable, and must be
 /// `co_await`ed in the same expression that created it.
 ///
-/// Using `NowTask` by default brings considerable safety benefits.  With
+/// Using `now_task` by default brings considerable safety benefits.  With
 /// `Task`, the following would be anti-patterns that cause dangling reference
-/// bugs, but with `NowTask`, C++ lifetime extension rules ensure that they
+/// bugs, but with `now_task`, C++ lifetime extension rules ensure that they
 /// simply work.
 ///   - Pass-by-reference into coroutines.
 ///   - Ephemeral coro lambdas with captures.
 ///   - Coro lambdas with capture-by-reference.
 ///
+/// Q: Is it `now_task` or `NowTask`?
+///
+/// A: In order to ease adoption, both forms will compile, but `now_task` is
+///    primary.  Both are intended to eventually be renamed to simply
+///    `folly::coro::task`, while the current movable, delayed-awaitable task
+///    will be renamed to `unsafe_task`, and largely superseded by
+///    `async_closure` and various `safe_task` flavors.
+///
 /// Notes:
-///   - (subject to change) Unlike `SafeTask`, `NowTask` does NOT check
-///     `safe_alias_of` for the return type `T`.  `NowTask` is essentially an
+///   - (subject to change) Unlike `safe_task`, `now_task` does NOT check
+///     `safe_alias_of` for the return type `T`.  `now_task` is essentially an
 ///     immediate async function -- it satisfies the structured concurrency
 ///     maxim of "lexical scope drives both control flow & lifetime".  That
 ///     lowers the odds that returned pointers/references are unexpectedly
@@ -50,105 +58,134 @@
 class BackgroundTask;
 
 template <typename T = void>
-class NowTask;
+class now_task;
 
 template <typename T = void>
-class NowTaskWithExecutor;
+class now_task_with_executor;
 
+// Backwards-compatibility shims
+template <typename T = void>
+using NowTask = now_task<T>;
+template <typename T = void>
+using NowTaskWithExecutor = now_task_with_executor<T>;
+
 namespace detail {
 template <typename T>
-struct NowTaskWithExecutorCfg : DoesNotWrapAwaitable {
+struct now_task_with_executor_cfg : DoesNotWrapAwaitable {
   using InnerTaskWithExecutorT = TaskWithExecutor<T>;
-  using WrapperTaskT = NowTask<T>;
+  using WrapperTaskT = now_task<T>;
 };
 template <typename T>
-using NowTaskWithExecutorBase =
-    AddMustAwaitImmediately<TaskWithExecutorWrapperCrtp<
-        NowTaskWithExecutor<T>,
-        detail::NowTaskWithExecutorCfg<T>>>;
+using now_task_with_executor_base =
+    ext::wrap_must_use_immediately_t<TaskWithExecutorWrapperCrtp<
+        now_task_with_executor<T>,
+        detail::now_task_with_executor_cfg<T>>>;
 } // namespace detail
 
 template <typename T>
-class FOLLY_NODISCARD NowTaskWithExecutor final
-    : public detail::NowTaskWithExecutorBase<T> {
+class [[nodiscard]]
+now_task_with_executor final : public detail::now_task_with_executor_base<T> {
  protected:
-  using detail::NowTaskWithExecutorBase<T>::NowTaskWithExecutorBase;
+  using detail::now_task_with_executor_base<T>::now_task_with_executor_base;
 
   template <safe_alias, typename>
   friend class BackgroundTask; // for `unwrapTaskWithExecutor`, remove later
+ public:
+  [[deprecated(
+      "`as_unsafe()` is provided as an escape hatch for interoperating with "
+      "older futures-based code, or other places not yet compatible with "
+      "true structured concurrency patterns. Beware, the full `Task` API "
+      "abounds with footguns like `start()` and `semi()` -- including UB, "
+      "leaks, and lost errors. See `folly/coro/safe/docs/AsUnsafe.md` for "
+      " safe migration patterns.")]]
+  TaskWithExecutor<T> as_unsafe() && {
+    return std::move(*this).unwrapTaskWithExecutor();
+  }
 };
 
 namespace detail {
 template <typename T>
-class NowTaskPromise final
-    : public TaskPromiseWrapper<T, NowTask<T>, TaskPromise<T>> {};
+class now_task_promise final
+    : public TaskPromiseWrapper<T, now_task<T>, TaskPromise<T>> {};
 template <typename T>
-struct NowTaskCfg : DoesNotWrapAwaitable {
+struct now_task_cfg : DoesNotWrapAwaitable {
   using ValueT = T;
   using InnerTaskT = Task<T>;
-  using TaskWithExecutorT = NowTaskWithExecutor<T>;
-  using PromiseT = NowTaskPromise<T>;
+  using TaskWithExecutorT = now_task_with_executor<T>;
+  using PromiseT = now_task_promise<T>;
 };
 template <typename T>
-using NowTaskBase =
-    AddMustAwaitImmediately<TaskWrapperCrtp<NowTask<T>, detail::NowTaskCfg<T>>>;
+using now_task_base = ext::wrap_must_use_immediately_t<
+    TaskWrapperCrtp<now_task<T>, detail::now_task_cfg<T>>>;
 } // namespace detail
 
 template <safe_alias, typename>
-class SafeTask;
+class safe_task;
 
 template <safe_alias S, typename U>
-auto toNowTask(SafeTask<S, U>);
+auto to_now_task(safe_task<S, U> t);
 
 template <typename T>
-class FOLLY_CORO_TASK_ATTRS NowTask final : public detail::NowTaskBase<T> {
+class FOLLY_CORO_TASK_ATTRS now_task final : public detail::now_task_base<T> {
  protected:
-  using detail::NowTaskBase<T>::NowTaskBase;
+  using detail::now_task_base<T>::now_task_base;
 
   template <typename U> // can construct
-  friend auto toNowTask(Task<U>);
+  friend auto to_now_task(Task<U>);
   template <safe_alias S, typename U> // can construct
-  friend auto toNowTask(SafeTask<S, U>);
+  friend auto to_now_task(safe_task<S, U>);
   template <typename U> // can construct & `unwrapTask`
-  friend auto toNowTask(NowTask<U>);
+  friend auto to_now_task(now_task<U>);
+
+ public:
+  [[deprecated(
+      "`as_unsafe()` is provided as an escape hatch for interoperating with "
+      "older futures-based code, or other places not yet compatible with "
+      "true structured concurrency patterns. Beware, the full `Task` API "
+      "abounds with footguns like `start()` and `semi()` -- including UB, "
+      "leaks, and lost errors. See `folly/coro/safe/docs/AsUnsafe.md` for "
+      " safe migration patterns.")]]
+  Task<T> as_unsafe() && {
+    return std::move(*this).unwrapTask();
+  }
 };
 
-// NB: `toNowTask(SafeTask)` is in `SafeTask.h` to avoid circular deps.
+// NB: `to_now_task(safe_task)` is in `SafeTask.h` to avoid circular deps.
 template <typename T>
-auto toNowTask(Task<T> t) {
-  return NowTask<T>{std::move(t)};
+auto to_now_task(Task<T> t) {
+  return now_task<T>{std::move(t)};
 }
 template <typename T>
-auto toNowTask(NowTask<T> t) {
-  return NowTask<T>{std::move(t).unwrapTask()};
+auto to_now_task(now_task<T> t) {
+  return now_task<T>{std::move(t).unwrapTask()};
 }
 
 // Apparently, Clang 15 has a bug in prvalue semantics support, so it cannot
 // return immovable coroutines.
 #if !defined(__clang__) || __clang_major__ > 15
 
-/// Make a `NowTask` that trivially returns a value.
+/// Make a `now_task` that trivially returns a value.
 template <class T>
-NowTask<T> makeNowTask(T t) {
+now_task<T> make_now_task(T t) {
   co_return t;
 }
 
-/// Make a `NowTask` that trivially returns no value
-inline NowTask<> makeNowTask() {
+/// Make a `now_task` that trivially returns no value
+inline now_task<> make_now_task() {
   co_return;
 }
-/// Same as makeNowTask(). See Unit
-inline NowTask<> makeNowTask(Unit) {
+/// Same as make_now_task(). See Unit
+inline now_task<> make_now_task(Unit /*unused*/) {
   co_return;
 }
 
-/// Make a `NowTask` that will trivially yield an exception.
+/// Make a `now_task` that will trivially yield an exception.
 template <class T>
-NowTask<T> makeErrorNowTask(exception_wrapper ew) {
+now_task<T> make_error_now_task(exception_wrapper ew) {
   co_yield co_error(std::move(ew));
 }
 
-#endif // no `makeNowTask` on old/buggy clang
+#endif // no `make_now_task` on old/buggy clang
 
 } // namespace folly::coro
 
diff --git a/folly/folly/coro/safe/SafeAlias.h b/folly/folly/coro/safe/SafeAlias.h
deleted file mode 100644
--- a/folly/folly/coro/safe/SafeAlias.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/Traits.h>
-#include <folly/lang/SafeAlias-fwd.h>
-
-#include <type_traits>
-
-namespace folly {
-template <typename> // Forward-decl to keep `RValueReferenceWrapper.h` dep-free
-class rvalue_reference_wrapper;
-} // namespace folly
-
-/*
-"Aliasing" is indirect access to memory via pointers or references.  It is
-the major cause of memory-safety bugs in C++, but is also essential for
-writing correct & performant C++ programs.  Fortunately,
-  - Much business logic can be written in a pure-functional style, where
-    only value semantics are allowed.  Such code is easier to understand,
-    and has much better memory-safety.
-  - When references ARE used, the most common scenario is passing a
-    reference from a parent lexical scope to descendant scopes.
-
-`safe_alias_of_v` is a _heuristic_ to check whether a type is likely to be
-memory-safe in the above settings.  The `safe_alias` enum shows a hierarchy
-of memory safety, but you only need to know about two:
-  - `unsafe` -- e.g. raw pointers or references, and
-  - `maybe_value` -- `int`, `std::pair<int, char>`, or `std::unique_ptr<Foo>`.
-
-A user can easily bypass the heuristic -- since C++ lacks full reflection,
-it is impossible to make this bulletproof.  Our goals are much more modest:
-  - Make unsafe aliasing **more** visible in code review, and
-  - Encourage programmers to use safe semantics by default.
-
-The BIG CAVEATS are:
-
- - The "composition hole" -- i.e. aliasing hidden in structures.  We can't see
-   unsafe class members, so `UnsafeStruct` below will be deduced to have
-   `maybe_value` safety unless you specialize `safe_alias_of<UnsafeStruct>`.
-     struct UnsafeStruct { int* rawPtr; };
-   Future: Perhaps with C++26 reflection, this could be fixed.
-
-   The "lambda hole" is a particularly easy instance of the "composition hole".
-   With lambda captures, a parent needs just one `&` to let a child pass a
-   soon-to-be-dangling reference up the stack.  E.g. this compiles:
-       int* badPtr;
-       auto t = async_closure(
-         // LAMBDA HOLE: We can't tell this callable object is unsafe!
-         bound_args{[&](int p) { *badPtr = p; }},
-         [](auto fn) -> ClosureTask<void> {
-           int i = 5;
-           fn(i); // FAILURE: Dereferencing uninitialized `badPtr`.
-           co_return;
-         });
-
- - Nullability & pointer stability: These hazards are not very specific to
-   coroutines, and the current design of `folly/coro/safe` largely avoids
-   unstable containers.  Nonetheless, you must beware container mutation is
-   an easy way to invalidate `safe_alias` memory-safety measurements.  For
-   example `unique_ptr<int>` and `vector<int>` have `maybe_value` safety.
-   However, if you mutate them (`reset()`, `clear()`, etc), that would
-   invalidate any async references (e.g.  `Captures.h`) pointing inside.
-   Luckily, there's no implicit way of getting a safe reference to inside
-   regular containers.  However, it is recommended to reduce accidental
-   nullability where possible.  For example, `capture<unique_ptr<T>>`
-   exposes `reset()`, but `capture_indirect<unique_ptr<T>>` hides it behind
-   `get_underlying_unsafe()`.  Better yet, `capture<AsyncObjectPtr<T>>`
-   blocks the underlying `clear()` method entirely.
-
-If you need to bypass this control, prefer the `manual_safe_*` wrappers
-below, instead of writing a custom workaround.  Always explain why it's safe.
-
-To teach `safe_alias_of` about your type, include `SafeAlias-fwd.h` and either:
- 1) Add a member type alias to your class:
-    using using folly_private_safe_alias_t = safe_alias_constant<...>;
- 2) Specialize `folly::safe_alias_of<YourT>`.
-
-When adding `safe_alias` annotations to types, stick to these principles:
-  - Always mark the `safe_alias` level in the header that declares your type.
-    For `std` types you cannot change, add the specialization here, in
-    `SafeAlias.h`.  Since we cannot forward-declare from `std`, this
-    unfortunately imposes a tradeoff between build cost and safety.  Commonly
-    used containers are worth the cost.  For less-commonly used containers, we
-    could develop a multi-header setup, plus some linter coverage to ensure the
-    right headers ultimately do get included.
-  - Only use `maybe_value` if your type ACTUALLY follows value semantics.
-  - Unless you're implementing an `async_closure`-integrated type, it is VERY
-    unlikely that you should use anything besides `unsafe` or `maybe_value`.
-  - Use `safe_alias_of_pack` to aggregate safety for a multi-part type.
-*/
-namespace folly {
-
-// Types are `maybe_value` unless otherwise specified.  Note that
-// `SafeAlias-fwd.h` already marks raw pointers & refs as `unsafe`, and peels
-// off CV qualifiers from the type being tested.
-//
-// See also: `safe_alias_of_v`.
-//
-// As explained in `SafeAlias-fwd.h`, do NOT move this to the `fwd` header.  To
-// guarantee safety, this permissive primary template must be colocated with
-// the other specializations below.
-template <typename T, typename /*SFINAE*/>
-struct safe_alias_of : safe_alias_constant<safe_alias::maybe_value> {};
-
-// Reference wrappers are unsafe.
-template <typename T>
-struct safe_alias_of<std::reference_wrapper<T>>
-    : safe_alias_constant<safe_alias::unsafe> {};
-template <typename T>
-struct safe_alias_of<folly::rvalue_reference_wrapper<T>>
-    : safe_alias_constant<safe_alias::unsafe> {};
-
-// Let `safe_alias_of_v` recursively inspect `std` containers that are likely
-// to be involved in bugs.  If you encounter a memory-safety issue that
-// would've been caught by this, feel free to extend this.
-template <typename... As>
-struct safe_alias_of<std::tuple<As...>> : safe_alias_of_pack<As...> {};
-template <typename... As>
-struct safe_alias_of<std::pair<As...>> : safe_alias_of_pack<As...> {};
-template <typename... As>
-struct safe_alias_of<std::vector<As...>> : safe_alias_of_pack<As...> {};
-
-// Recursing into `tag_t<>` type lists is nice for metaprogramming
-template <typename... As>
-struct safe_alias_of<::folly::tag_t<As...>> : safe_alias_of_pack<As...> {};
-
-// IMPORTANT: If you use the `manual_safe_` escape-hatch wrappers, you MUST
-// comment with clear proof of WHY your usage is safe.  The goal is to
-// ensure careful review of such code.
-//
-// Careful: With the default `Safety`, the contained value or reference can be
-// passed anywhere -- the wrapper pretends to be a value type.
-//
-// If you know a more restrictive safety level for your ref, annotate it to
-// improve safety:
-//  - `after_cleanup_ref` for things owned by co_cleanup args of this closure,
-//  - `co_cleanup_safe_ref` for refs to non-cleanup args owned by this closure,
-//    or any ancestor closure.
-//
-// The types are public since they may occur in user-facing signatures.
-
-template <safe_alias, typename T>
-struct manual_safe_ref_t : std::reference_wrapper<T> {
-  using typename std::reference_wrapper<T>::type;
-  using std::reference_wrapper<T>::reference_wrapper;
-};
-
-template <safe_alias, typename T>
-struct manual_safe_val_t {
-  using type = T;
-
-  template <typename... Args>
-  manual_safe_val_t(Args&&... args) : t_(static_cast<Args&&>(args)...) {}
-  template <typename Fn>
-  manual_safe_val_t(std::in_place_type_t<T>, Fn fn) : t_(fn()) {}
-
-  T& get() & noexcept { return t_; }
-  operator T&() & noexcept { return t_; }
-  const T& get() const& noexcept { return t_; }
-  operator const T&() const& noexcept { return t_; }
-  T&& get() && noexcept { return std::move(t_); }
-  operator T&&() && noexcept { return std::move(t_); }
-
- private:
-  T t_;
-};
-
-template <safe_alias Safety = safe_alias::maybe_value, typename T = void>
-auto manual_safe_ref(T& t) {
-  return manual_safe_ref_t<Safety, T>{t};
-}
-template <safe_alias Safety = safe_alias::maybe_value, typename T>
-auto manual_safe_val(T t) {
-  return manual_safe_val_t<Safety, T>{std::move(t)};
-}
-template <safe_alias Safety = safe_alias::maybe_value, typename Fn>
-auto manual_safe_with(Fn&& fn) {
-  using FnRet = decltype(static_cast<Fn&&>(fn)());
-  return manual_safe_val_t<Safety, FnRet>{
-      std::in_place_type<FnRet>, static_cast<Fn&&>(fn)};
-}
-
-template <safe_alias S, typename T>
-struct safe_alias_of<manual_safe_ref_t<S, T>> : safe_alias_constant<S> {};
-template <safe_alias S, typename T>
-struct safe_alias_of<manual_safe_val_t<S, T>> : safe_alias_constant<S> {};
-
-} // namespace folly
diff --git a/folly/folly/coro/safe/SafeTask.h b/folly/folly/coro/safe/SafeTask.h
--- a/folly/folly/coro/safe/SafeTask.h
+++ b/folly/folly/coro/safe/SafeTask.h
@@ -18,78 +18,88 @@
 
 #include <folly/coro/TaskWrapper.h>
 #include <folly/coro/safe/NowTask.h>
-#include <folly/coro/safe/SafeAlias.h>
+#include <folly/lang/SafeAlias.h>
 
 #if FOLLY_HAS_IMMOVABLE_COROUTINES
 
 namespace folly::coro {
 
-/// Why is `SafeTask.h` useful? See `SafeTask.md`.
+/// Why is `SafeTask.h` useful?
 ///
-/// Typically, you will not use `SafeTask` directly.  Instead, choose one of
+/// `now_task` from `NowTask.h` should be your default.  This immovable task
+/// should always be awaited in the full-expression that created it,
+/// eliminating most classes of lifetime-safety bugs.
+///
+/// If you need a MOVABLE task with compile-time safety checks, read on.
+///
+/// Typically, you will not use `safe_task` directly.  Instead, choose one of
 /// the type-aliases below, following `APIBestPractices.md` guidance.  Briefly:
-///   - `ValueTask`: Use if your coro only takes value-semantic args.
-///   - `MemberTask`: Use for all non-static member functions.  Can be
-///     awaited immediately (like `NowTask`), or wrapped in an
-///     `async_closure` to support less-structured concurrency -- including
-///     scheduling on a background scope belonging to the object.
-///   - `ClosureTask`: Use if your coro is called via `async_closure`.
-///   - `CoCleanupSafeTask`: Use for tasks that can be directly scheduled on a
-///     `SafeAsyncScope`.
-///   - (not in `SafeTask.h`) `NowTask`: All other coros.  This requires the
-///     task to always be awaited in the expression that created it,
-///     eliminating a variety of common dangling reference bugs.
-///   - `AutoSafeTask`: Generic coros where you want the argument & return
-///     types to automatically branch between a `NowTask` and a `SafeTask`.
 ///
-/// `SafeTask` is a thin wrapper around `folly::coro::Task` that uses
+///   `value_task`:
+///   Use if your coro only takes value-semantic args.
+///
+///   `member_task`:
+///   Use for non-static member functions.  Can be awaited immediately (like
+///   `now_task`), or wrapped in an `async_closure` to support less-structured
+///   concurrency -- including scheduling on a background scope belonging to
+///   the object.  IMPORTANT: adding a `member_task` to a class requires it to
+///   have an **explicit** `folly/lang/SafeAlias.h` annotation.
+///
+///   `closure_task`:
+///   Use if your coro is called via `async_closure`.  Outside of closures,
+///   this behaves like `now_task`.
+///
+///   `co_cleanup_safe_task`:
+///   Use for tasks that can be directly scheduled on a `safe_async_scope`.
+///
+///   `auto_safe_task`:
+///   Generic coros where you want the argument & return types to automatically
+///   choose between a `now_task` and a `safe_task`.
+///
+/// `safe_task` is a thin wrapper around `folly::coro::Task` that uses
 /// `safe_alias_of` to enforce some compile-time guarantees:
-///   - The `SafeTask` has `safe_alias_of` memory safety at least as high as
+///   - The `safe_task` has `safe_alias_of` memory safety at least as high as
 ///     the coro's arguments.  In particular, no args are taken by reference.
 ///   - Regardless of the task's declared safety, the coro's return must
-///     have safety `maybe_value` (explained in `SafeTaskRetAndArgs`).
+///     have safety `maybe_value` (explained in `safe_task_ret_and_args`).
 ///   - The coroutine is NOT a stateful callable -- this prohibits lambda
 ///     captures, since those are a very common cause of coro memory bugs.
 template <safe_alias, typename = void>
-class SafeTask;
+class safe_task;
 template <safe_alias, typename = void>
-class SafeTaskWithExecutor;
+class safe_task_with_executor;
 
-// A `SafeTask` whose args and return type follow value semantics.
+// A `safe_task` whose args and return type follow value semantics.
 template <typename T = void>
-using ValueTask = SafeTask<safe_alias::maybe_value, T>;
+using value_task = safe_task<safe_alias::maybe_value, T>;
 
-// A `SafeTask` that can be added to `SafeAsyncScope`, and may run during
+// A `safe_task` that can be added to `safe_async_scope`, and may run during
 // closure cleanup.  Its content must therefore be `co_cleanup_safe_ref`-safe.
 template <typename T = void>
-using CoCleanupSafeTask = SafeTask<safe_alias::co_cleanup_safe_ref, T>;
+using co_cleanup_safe_task = safe_task<safe_alias::co_cleanup_safe_ref, T>;
 
-// Use `ClosureTask` as the inner coro type for tasks meant to ALWAYS be
-// wrapped in an `async_closure`.
-//
-// Outside of a closure, a `ClosureTask` is immovable.  If you are wanting to
-// move a `ClosureTask`, construct it via an async closure, and you'll get back
-// a `SafeTask` with safety measurements reflecting the safety of its args.
+// Use `closure_task` as the inner coro type for tasks meant to be wrapped in
+// an `async_closure` or `async_now_closure`.  The closure code will measure
+// the safety of its args, and return a correct `safe_task`.
 //
-// If your use-case calls for a `SafeTask` that is sometimes wrapped in a
-// closure, and sometimes is constructed without a closure, you might add a
-// `MinClosureSafeTask` type alias for `closure_min_arg_safety`.
+// Without a surrounding closure call, a `closure_task` is immovable, and works
+// like `now_task`, but with some restrictions on its arg & return types.
 //
-// Immovability rationale: `ClosureTask` is implemented as a `SafeTask` for
+// Immovability rationale: `closure_task` is implemented as a `safe_task` for
 // reasons explained in the next paragraph.  But, its safety contract is weaker
 // than that of the usual closure (it can take `capture<Val>`, which should
 // never be moved) -- immovability is meant to reduce the odds of misuse.
 // Making it truly opaque / not semi-awaitable would be a stronger safeguard,
 // but that requires extra complexity even just so that
-// `AsNoexcept<ClosureTask<>> foo()` would compile.
+// `value_or_fatal<closure_task<>> foo()` would compile.
 //
-// "ClosureTask is a SafeTask" rationale: `async_closure` cannot emit a
-// `SafeTask` without the inner coro being a `SafeTask` -- otherwise it could
+// "closure_task is a safe_task" rationale: `async_closure` cannot emit a
+// `safe_task` without the inner coro being a `safe_task` -- otherwise it could
 // not guarantee that none of the args are taken by reference.  Conveniently,
-// `SafeTask` also checks the return type is safe, and the coro's callable is
-// stateless, so `async_closure` can skip those checks.
+// `safe_task` also checks the return type is safe, and the coro's callable is
+// stateless, so `async_closure` can rely on those checks.
 //
-// This `ClosureTask` implementation uses a `safe_alias` level safer than
+// This `closure_task` implementation uses a `safe_alias` level safer than
 // `unsafe` to get all of the above `SafeAlias` checks.  The level also has
 // to be less safe than `shared_cleanup` so we can treat these differently:
 //  - `capture<Value>` (safety `unsafe_closure_internal`) should stay in the
@@ -100,101 +110,114 @@
 //  - By the way, `co_cleanup_capture<Value>` should never be moved from the
 //    owning closure that's responsible for its cleanup.
 template <typename T = void>
-using ClosureTask = SafeTask<safe_alias::unsafe_closure_internal, T>;
+using closure_task = safe_task<safe_alias::unsafe_closure_internal, T>;
 
-// A `MemberTask` is a hybrid of `SafeTask` and `NowTask`, intended to make
-// non-static member coroutines safer.
-//   - It **is** a `SafeTask`, thereby forbidding `safe_alias::unsafe`
-//     arguments, and unsafe return types.  However, since the callable of
-//     member coros is inherently stateful, it is special-cased to omit the
-//     safety checks on the implicit object parameter.
-//   - It is immovable like `NowTask`, which makes typical "structured
-//     concurrency" usage of coroutines quite safe (see `NowTask.h`).
-//     `MemberTask` needs this, since members take `this`, whose lifetime is
-//     unknown -- i.e.  outside of async closure usage, a `MemberTask` is just
-//     a `NowTask`.
+// A `member_task` is nearly identical to `closure_task`, except that it's
+// usable with non-static member coroutines on **stateful** classes.
+//   - In `async_closure`, it behaves like `closure_task`, with one
+//     extra constraint -- its implicit object parameter must refer to a class
+//     with an explicitly annotated `safe_alias_of` (see `SafeAlias.h`).
+//   - Without `async_closure`, it is immediately-awaitable, like `now_task`.
+//   - Since it is also a `safe_task`, it forbids `safe_alias::unsafe`
+//     arguments, and unsafe return types.
 //
-// For more complex usage (background tasks, async RAII), `MemberTask` has a
-// special calling convention in `AsyncClosure.h`:
+// To construct `async_closure`s with `member_task`s (for background or scope
+// tasks), use this special calling convention:
 //
-//   async_closure(bound_args{obj, args...}, FOLLY_INVOKE_MEMBER(memberFnName))
+//   async_closure(bind::args{obj, args...}, FOLLY_INVOKE_MEMBER(memberFnName))
 //
-// Like any async closure, this safety-checks the now-explicit object param,
-// and produces a movable `SafeTask` of the safety level determined from the
-// arguments.  This integration lets us safely schedule member coros on
-// `SafeAsyncScope`, pass `co_cleanup` args into such coros, etc.
+// This closure will safety-check the now-explicit object param, and produce a
+// movable `safe_task` of the safety level determined by the arguments.  This
+// integration lets us safely schedule member coros on `safe_async_scope`, pass
+// `co_cleanup` args into such coros, etc.
+//
+// Design note: Fundamentally, the reason that we have `member_task`, and
+// cannot just use `closure_task` for class members, is that C++20 does not let
+// a coroutine function distinguish (i) an "implicit object param" from (ii) a
+// regular lvalue reference.  For (i), `member_task` requires `async_closure`
+// to invoke it with `FOLLY_INVOKE_MEMBER`, so in this case we just need the
+// first arg to be a reference-to-a-safe-object.  In case (ii), such as
+// `closure_task`, a reference-to-a-safe-object is definitely unsafe, whereas a
+// reference to a stateless/empty object is safe enough.
 template <typename T>
-using MemberTask = SafeTask<safe_alias::unsafe_member_internal, T>;
+using member_task = safe_task<safe_alias::unsafe_member_internal, T>;
 
 // NB: There are some `async_closure`-specific values of `safe_alias` that
-// do not yet have a `SafeTask` alias.  That's because they haven't come up
+// do not yet have a `safe_task` alias.  That's because they haven't come up
 // in user-facing type signatures.
 
 namespace detail {
 template <typename T, safe_alias Safety>
-using AutoSafeTaskImpl = std::conditional_t<
+using auto_safe_task_impl = std::conditional_t<
     // This checks both args & the return value because we want to avoid this
-    // resolving to a `SafeTask` that won't actually compile.
+    // resolving to a `safe_task` that won't actually compile.
     (Safety >= safe_alias::closure_min_arg_safety &&
-     safe_alias_of_v<T> >= safe_alias::maybe_value),
-    SafeTask<Safety, T>,
-    NowTask<T>>;
+     lenient_safe_alias_of_v<T> >= safe_alias::maybe_value),
+    safe_task<Safety, T>,
+    now_task<T>>;
 }
 
-/// Coros declared as `SafeTask<Safety, T>` will satisfy the strong
+/// Coros declared as `safe_task<Safety, T>` will satisfy the strong
 /// constraints above, or fail with a compile error.
 ///
 /// The safety of a coroutine template may vary depending on the args or
 /// return type, meaning that the user can't actually pick a fixed
 /// safety level for their generic coro.
 ///
-/// Instead, the generic coro can return `AutoSafeTask<ReturnT,
+/// Instead, the generic coro can return `auto_safe_task<ReturnT,
 /// SafetyArgs...>`, where `SafetyArgs` is (typically) the subset of the
 /// coroutine's argument types that may affect safety.
 ///
-/// `AutoSafeTask` has a Significant Caveat -- you can't use it with
+/// `auto_safe_task` has a Significant Caveat -- you can't use it with
 /// non-`static` member functions -- the implicit object parameter is unsafe
 /// (as it should be).  And if you do use it, you will get a compile-time
-/// error instead of a `NowTask`, simply because this type-function has no
+/// error instead of a `now_task`, simply because this type-function has no
 /// access to the callable.  See `APIBestPractices.md` for workarounds.
 template <typename T, typename... SafetyArgs>
-using AutoSafeTask =
-    detail::AutoSafeTaskImpl<T, safe_alias_of_pack<SafetyArgs...>::value>;
+using auto_safe_task = detail::auto_safe_task_impl<
+    T,
+    // Same logic as `lenient_safe_alias_of_v`
+    safe_alias_of_pack<safe_alias::maybe_value, SafetyArgs...>::value>;
 
 namespace detail {
 
 struct SafeTaskTest;
 
 template <safe_alias ArgSafety, typename RetT, typename... Args>
-concept SafeTaskRetAndArgs = ((safe_alias_of_v<Args> >= ArgSafety) && ...) &&
+concept safe_task_ret_and_args =
+    ((lenient_safe_alias_of_v<Args> >= ArgSafety) && ...) &&
     // In the event that you need a child scope to return a reference to
     // something owned by a still-valid ancestor scope, we don't have a good
     // way to detect this automatically.  To work around, use a `manual_safe_*`
     // wrapper in `SafeAlias.h`, and comment why it is safe.
-    (safe_alias_of_v<RetT> >= safe_alias::maybe_value);
+    (lenient_safe_alias_of_v<RetT> >= safe_alias::maybe_value);
 
 template <typename T>
 concept is_stateless_class_or_func =
-    (std::is_class_v<T> && std::is_empty_v<T>) ||
+    // `require_sizeof` avoids `is_empty` UB on incomplete types
+    (require_sizeof<T> >= 0 && std::is_empty_v<T>) ||
     (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>);
 
 template <safe_alias, typename...>
-inline constexpr bool IsSafeTaskValid = false;
+inline constexpr bool is_safe_task_valid = false;
 // Coros taking 0 args can't be methods (no implicit object parameter),
 // so their safety is determined by the return type.
 template <safe_alias ArgSafety, typename RetT>
-inline constexpr bool IsSafeTaskValid<ArgSafety, RetT> =
-    SafeTaskRetAndArgs<ArgSafety, RetT>;
+inline constexpr bool is_safe_task_valid<ArgSafety, RetT> =
+    safe_task_ret_and_args<ArgSafety, RetT>;
 // Inspect the first argument, which can be an implicit object parameter, to
-// allow stateless callables (like lambdas), but to prohibit stateful
-// callables (these can contain unsafe aliasing in their state, which we
-// can't inspect).  If you need to make `SafeTask`s from a stateful object,
-// pass `capture<Ref>` to a static func, and check out `AsyncObject.h`.
+// allow stateless callables (like lambdas), but to prohibit stateful callables
+// (these can contain unsafe aliasing in their state, which we can't inspect
+// automatically).  If you need to make `safe_task`s from a stateful object,
+// ideas include:
+//   - Pass a `capture<YourClass&>` to a static member `YourClass::func`
+//   - Explicitly mark `safe_alias_of<YourClass>` and use `member_task`.
+//   - Future: Check out `AsyncObject.h`.
 //
 // How this works: With >= 1 args in the pack, the `First` argument
 // **could** be an implicit object parameter.  We don't know if it is, but
 // we do know that any such parameter has type lvalue reference, which means
-// that it would fail `SafeTaskRetAndArgs<RetT, First, Args...>`.
+// that it would fail `safe_task_ret_and_args<RetT, First, Args...>`.
 //
 // This test accepts any `First` that is an lref to a stateless class
 // or function -- that is, it returns `true` if the first arg is either:
@@ -204,57 +227,85 @@
 // pass a stateless class by reference, if it's the first param.  This
 // should be harmless in practice.
 //
-// FIXME: It should (?) be fine to simplify this scenario by having
-// `SafeAlias` mark as "safe" all references-to-empty-classes, and all
-// function pointers.  Then, only a shortened comment would survive.
-//
-// For `MemberTask`, `First` is assumed to be the implicit object parameter.
-// This cannot be safe, so we don't check it, and instead rely on
-// `MemberTask`'s usage restrictions (see also `SafeTaskBaseTrait`).
+// For `member_task`, `First` is assumed to be the implicit object parameter.
+// We cannot deduce its safety, so we use `strict_safe_alias_of_v` to force the
+// user to explicitly mark this class -- incidentally, this precludes
+// `member_task` lambdas.  It is safe for `member_task` to treat
+// a ref-to-a-safe-object as safe, since it is usable only:
+//   - As a free coro, where it acts like a restricted `now_task`
+//   - Via `async_closure`, which enforces a `FOLLY_INVOKE_MEMBER` calling
+//     convention that either takes a safe capture-ref, or makes the closure
+//     take ownership of the closure.
+// We MUST NOT apply the same treatment to `closure_task`, since it cannot
+// ensure that the ref itself is safe (even if the referenced type is).
 template <safe_alias ArgSafety, typename RetT, typename First, typename... Args>
-inline constexpr bool IsSafeTaskValid<ArgSafety, RetT, First, Args...> =
-    ((ArgSafety == safe_alias::unsafe_member_internal) ||
-     (std::is_lvalue_reference_v<First> &&
+inline constexpr bool is_safe_task_valid<ArgSafety, RetT, First, Args...> =
+    (std::is_lvalue_reference_v<First> &&
+     ((ArgSafety == safe_alias::unsafe_member_internal &&
+       strict_safe_alias_of_v<std::remove_reference_t<First>> >=
+           // This `std::max` requires the implicit object parameter of
+           // `member_task`s to have safety `> shared_cleanup`.  In effect,
+           // this prohibits `member_task`s from being added to classes that
+           // store `co_cleanup_capture` refs, or (equivalently) to
+           // `async_object`s.  This tradeoff is required so that
+           // `async_now_closure()` of `member_task`s is not ALWAYS forced to
+           // apply shared-cleanup downgrades to its arguments.  The details
+           // are in `async_closure_safeties_and_bindings`.  If you find
+           // yourself blocked by this restriction, my best idea is:
+           // (1) Add another `co_cleanup_member_task` (better name TBD), with
+           //     a new safety level `< unsafe_closure_internal`.
+           // (2) Scan all mentions of `unsafe_closure_internal` to make sure
+           //     nothing assumes it is the lowest "safe" level. Importantly,
+           //     the `bind_captures_to_closure` logic related to
+           //     shared-cleanup in `async_now_closure` would continue to
+           //     test for `unsafe_closure_internal`, NOT the new level.
+           // (3) Special-case `co_cleanup_member_task` in this check to
+           //     let its implicit object param be `<= shared_cleanup`.
+           std::max(
+               ArgSafety,
+               safe_alias{
+                   to_underlying(safe_alias::closure_min_arg_safety) + 1})) ||
       is_stateless_class_or_func<std::remove_reference_t<First>>))
-    ? SafeTaskRetAndArgs<ArgSafety, RetT, Args...>
-    : SafeTaskRetAndArgs<ArgSafety, RetT, First, Args...>;
+    ? safe_task_ret_and_args<ArgSafety, RetT, Args...>
+    : safe_task_ret_and_args<ArgSafety, RetT, First, Args...>;
 
 template <safe_alias ArgSafety, typename T, typename... Args>
-class SafeTaskPromise final
+class safe_task_promise final
     : public TaskPromiseWrapper<
           T,
-          SafeTask<ArgSafety, T>,
+          safe_task<ArgSafety, T>,
           detail::TaskPromise<T>> {
   // "Unsafe" is not a "safe" task any more.  In the future, we could have
-  // `SafeTask<unsafe, T>` act as `NowTask<T>`, but there's no present use
+  // `safe_task<unsafe, T>` act as `now_task<T>`, but there's no present use
   // for this uniformity, but there are benefits to explicitness.
   static_assert(
       ArgSafety > safe_alias::unsafe,
-      "Instead of making an unsafe `SafeTask`, use a `NowTask`, or "
+      "Instead of making an unsafe `safe_task`, use a `now_task`, or "
       "`async_now_closure()`");
 
  public:
   // IMPORTANT: If you alter this arrangement, do the "Manual test" inside
   // `returnsVoid` in `SafeTaskTest.cpp`.
   //
-  // This is a no-op wrapper.  It needs to exist because `IsSafeTaskValid`
-  // requires the coroutine function to be a complete type before checking
-  // if it's a stateless callable, and the easiest place to do that is in a
-  // class function that's guaranteed to be instantiated, such as this.
-  SafeTask<ArgSafety, T> get_return_object() noexcept {
-    // If your build failed here, your `SafeTask<>` coro declaration is
+  // This is a no-op wrapper.  It needs to exist because `is_safe_task_valid`
+  // requires the coroutine function to be a complete type before checking if
+  // it's a stateless callable in `is_safe_task_valid`.  This is a good place
+  // to do this, since this function is guaranteed to be instantiated.
+  safe_task<ArgSafety, T> get_return_object() noexcept {
+    // If your build failed here, your `safe_task<>` coro declaration is
     // invalid.  Specific causes for this failure:
     //   - One of the arguments, or the return value, contains "unsafe
     //     aliasing" -- see `SafeAlias.h` for the details.  Typical
     //     causes include raw pointers, references, reference wrappers, etc.
     //   - A stateful callable: lambda with captures, class with members, etc.
     static_assert(
-        detail::IsSafeTaskValid<ArgSafety, T, Args...>,
-        "Bad SafeTask: check for unsafe aliasing in arguments or return "
-        "type; also ensure your callable is stateless.");
+        detail::is_safe_task_valid<ArgSafety, T, Args...>,
+        "Bad safe_task: check for unsafe aliasing in arguments or return "
+        "type; also ensure your callable is stateless (or, in the case of "
+        "`member_task` -- explicitly specifies `safe_alias_of`).");
     return TaskPromiseWrapper<
         T,
-        SafeTask<ArgSafety, T>,
+        safe_task<ArgSafety, T>,
         detail::TaskPromise<T>>::get_return_object();
   }
 };
@@ -263,55 +314,55 @@
 auto bind_captures_to_closure(auto&&, auto);
 
 template <safe_alias ArgSafety, typename T>
-struct SafeTaskWithExecutorCfg : DoesNotWrapAwaitable {
+struct safe_task_with_executor_cfg : DoesNotWrapAwaitable {
   using InnerTaskWithExecutorT = TaskWithExecutor<T>;
-  using WrapperTaskT = SafeTask<ArgSafety, T>;
+  using WrapperTaskT = safe_task<ArgSafety, T>;
 };
 
 template <safe_alias, typename>
-struct SafeTaskWithExecutorBaseTraits;
+struct safe_task_with_executor_base_traits;
 
 template <safe_alias ArgSafety, typename T>
   requires(ArgSafety >= safe_alias::closure_min_arg_safety)
-struct SafeTaskWithExecutorBaseTraits<ArgSafety, T> {
+struct safe_task_with_executor_base_traits<ArgSafety, T> {
   using type = TaskWithExecutorWrapperCrtp<
-      SafeTaskWithExecutor<ArgSafety, T>,
-      SafeTaskWithExecutorCfg<ArgSafety, T>>;
+      safe_task_with_executor<ArgSafety, T>,
+      safe_task_with_executor_cfg<ArgSafety, T>>;
 };
 
-// `MemberTask` and `ClosureTask` are immovable.
+// `member_task` and `closure_task` are immovable.
 template <safe_alias ArgSafety, typename T>
   requires(ArgSafety < safe_alias::closure_min_arg_safety)
-struct SafeTaskWithExecutorBaseTraits<ArgSafety, T> {
-  using type = AddMustAwaitImmediately<TaskWithExecutorWrapperCrtp<
-      SafeTaskWithExecutor<ArgSafety, T>,
-      SafeTaskWithExecutorCfg<ArgSafety, T>>>;
+struct safe_task_with_executor_base_traits<ArgSafety, T> {
+  using type = ext::wrap_must_use_immediately_t<TaskWithExecutorWrapperCrtp<
+      safe_task_with_executor<ArgSafety, T>,
+      safe_task_with_executor_cfg<ArgSafety, T>>>;
 };
 
 template <safe_alias ArgSafety, typename T>
-struct SafeTaskCfg : DoesNotWrapAwaitable {
+struct safe_task_cfg : DoesNotWrapAwaitable {
   using ValueT = T;
   using InnerTaskT = Task<T>;
-  using TaskWithExecutorT = SafeTaskWithExecutor<ArgSafety, T>;
+  using TaskWithExecutorT = safe_task_with_executor<ArgSafety, T>;
   // There is no `promise_type` here because it's added by `coroutine_traits`
-  // below.  This is the mechanism that enables `SafeTaskPromise` to inspect
+  // below.  This is the mechanism that enables `safe_task_promise` to inspect
   // the specific arguments of the coroutine (including the implicit object
   // parameter), and fail the compilation if anything looks unsafe.
   using PromiseT = void;
 };
 
 template <safe_alias ArgSafety, typename T>
-struct SafeTaskBaseTraits {
+struct safe_task_base_traits {
   using type =
-      TaskWrapperCrtp<SafeTask<ArgSafety, T>, SafeTaskCfg<ArgSafety, T>>;
+      TaskWrapperCrtp<safe_task<ArgSafety, T>, safe_task_cfg<ArgSafety, T>>;
 };
 
-// `MemberTask` and `ClosureTask` are immovable.
+// `member_task` and `closure_task` are immovable.
 template <safe_alias ArgSafety, typename T>
   requires(ArgSafety < safe_alias::closure_min_arg_safety)
-struct SafeTaskBaseTraits<ArgSafety, T> {
-  using type = AddMustAwaitImmediately<
-      TaskWrapperCrtp<SafeTask<ArgSafety, T>, SafeTaskCfg<ArgSafety, T>>>;
+struct safe_task_base_traits<ArgSafety, T> {
+  using type = ext::wrap_must_use_immediately_t<
+      TaskWrapperCrtp<safe_task<ArgSafety, T>, safe_task_cfg<ArgSafety, T>>>;
 };
 
 } // namespace detail
@@ -320,70 +371,74 @@
 class BackgroundTask;
 
 // IMPORTANT: This omits `start()` because backgrounded tasks can easily
-// outlive the references they took, defeating the purpose of `SafeTask`.
+// outlive the references they took, defeating the purpose of `safe_task`.
 // See `BackgroundTask` instead.
 template <safe_alias ArgSafety, typename T>
-class FOLLY_NODISCARD SafeTaskWithExecutor final
-    : public detail::SafeTaskWithExecutorBaseTraits<ArgSafety, T>::type {
+class [[nodiscard]] safe_task_with_executor final
+    : public detail::safe_task_with_executor_base_traits<ArgSafety, T>::type {
  protected:
-  using detail::SafeTaskWithExecutorBaseTraits<ArgSafety, T>::type::type;
+  using detail::safe_task_with_executor_base_traits<ArgSafety, T>::type::type;
 
   template <safe_alias, typename>
   friend class BackgroundTask; // for `unwrapTaskWithExecutor()`, remove later
 
  public:
+  template <safe_alias>
   using folly_private_safe_alias_t = safe_alias_constant<ArgSafety>;
 
   [[deprecated(
-      "`asUnsafe()` is provided as an escape hatch for interoperating with "
+      "`as_unsafe()` is provided as an escape hatch for interoperating with "
       "older futures-based code, or other places not yet compatible with "
       "true structured concurrency patterns. Beware, the full `Task` API "
       "abounds with footguns like `start()` and `semi()` -- including UB, "
-      "leaks, and lost errors.")]]
-  TaskWithExecutor<T> asUnsafe() && {
+      "leaks, and lost errors. See `folly/coro/safe/docs/AsUnsafe.md` for "
+      " safe migration patterns.")]]
+  TaskWithExecutor<T> as_unsafe() && {
     return std::move(*this).unwrapTaskWithExecutor();
   }
 };
 
 template <safe_alias ArgSafety, typename T>
-class FOLLY_CORO_TASK_ATTRS SafeTask final
-    : public detail::SafeTaskBaseTraits<ArgSafety, T>::type {
+class FOLLY_CORO_TASK_ATTRS safe_task final
+    : public detail::safe_task_base_traits<ArgSafety, T>::type {
  protected:
   friend struct folly::coro::detail::SafeTaskTest; // to test `withNewSafety`
   template <safe_alias, typename>
-  friend class SafeTask; // `withNewSafety` makes a different `SafeTask`
+  friend class safe_task; // `withNewSafety` makes a different `safe_task`
   template <auto> // uses `withNewSafety`
   friend auto detail::bind_captures_to_closure(auto&&, auto);
   template <safe_alias Safety, typename U>
-  friend auto toNowTask(SafeTask<Safety, U>);
+  friend auto to_now_task(safe_task<Safety, U>);
 
   // The `async_closure` implementation is allowed to override the
-  // argument-deduced `safe_alias_of_v` for a `SafeTask` because
+  // argument-deduced `lenient_safe_alias_of_v` for a `safe_task` because
   // `capture_safety` marks some coro-stored `*capture*`s as `unsafe` even
   // though they're safe -- to discourage users from moving them.
   template <safe_alias NewSafety>
-  SafeTask<NewSafety, T> withNewSafety() && {
-    return SafeTask<NewSafety, T>{std::move(*this).unwrapTask()};
+  safe_task<NewSafety, T> withNewSafety() && {
+    return safe_task<NewSafety, T>{std::move(*this).unwrapTask()};
   }
 
  public:
-  using detail::SafeTaskBaseTraits<ArgSafety, T>::type::type;
+  using detail::safe_task_base_traits<ArgSafety, T>::type::type;
+  template <safe_alias>
   using folly_private_safe_alias_t = safe_alias_constant<ArgSafety>;
 
   [[deprecated(
-      "`asUnsafe()` is provided as an escape hatch for interoperating with "
+      "`as_unsafe()` is provided as an escape hatch for interoperating with "
       "older futures-based code, or other places not yet compatible with "
       "true structured concurrency patterns. Beware, the full `Task` API "
       "abounds with footguns like `start()` and `semi()` -- including UB, "
-      "leaks, and lost errors.")]]
-  Task<T> asUnsafe() && {
+      "leaks, and lost errors. See `folly/coro/safe/docs/AsUnsafe.md` for "
+      " safe migration patterns.")]]
+  Task<T> as_unsafe() && {
     return std::move(*this).unwrapTask();
   }
 };
 
 template <safe_alias Safety, typename T>
-auto toNowTask(SafeTask<Safety, T> t) {
-  return NowTask<T>{std::move(t).unwrapTask()};
+auto to_now_task(safe_task<Safety, T> t) {
+  return now_task<T>{std::move(t).unwrapTask()};
 }
 
 namespace detail {
@@ -399,18 +454,19 @@
 template <typename T>
 struct safe_task_traits<TaskWithExecutor<T>> : safe_task_traits<Task<T>> {};
 template <typename T>
-struct safe_task_traits<NowTask<T>> : safe_task_traits<Task<T>> {};
+struct safe_task_traits<now_task<T>> : safe_task_traits<Task<T>> {};
 template <typename T>
-struct safe_task_traits<NowTaskWithExecutor<T>> : safe_task_traits<Task<T>> {};
+struct safe_task_traits<now_task_with_executor<T>> : safe_task_traits<Task<T>> {
+};
 
 template <safe_alias ArgSafety, typename T>
-struct safe_task_traits<SafeTask<ArgSafety, T>> {
+struct safe_task_traits<safe_task<ArgSafety, T>> {
   static constexpr safe_alias arg_safety = ArgSafety;
   using return_type = T;
 };
 template <safe_alias ArgSafety, typename T>
-struct safe_task_traits<SafeTaskWithExecutor<ArgSafety, T>>
-    : safe_task_traits<SafeTask<ArgSafety, T>> {};
+struct safe_task_traits<safe_task_with_executor<ArgSafety, T>>
+    : safe_task_traits<safe_task<ArgSafety, T>> {};
 
 } // namespace detail
 
@@ -418,11 +474,11 @@
 
 template <folly::safe_alias ArgSafety, typename T, typename... Args>
 struct folly::coro::
-    coroutine_traits<folly::coro::SafeTask<ArgSafety, T>, Args...> {
-  // UGH: Pass `Args...` into `SafeTaskPromise` because at this point, the
+    coroutine_traits<folly::coro::safe_task<ArgSafety, T>, Args...> {
+  // UGH: Pass `Args...` into `safe_task_promise` because at this point, the
   // coroutine function is still an incomplete type, and can't be validated.
   using promise_type =
-      folly::coro::detail::SafeTaskPromise<ArgSafety, T, Args...>;
+      folly::coro::detail::safe_task_promise<ArgSafety, T, Args...>;
 };
 
 #endif
diff --git a/folly/folly/coro/safe/detail/AsyncClosure.h b/folly/folly/coro/safe/detail/AsyncClosure.h
--- a/folly/folly/coro/safe/detail/AsyncClosure.h
+++ b/folly/folly/coro/safe/detail/AsyncClosure.h
@@ -16,9 +16,9 @@
 
 #pragma once
 
-#include <folly/coro/Noexcept.h>
+#include <folly/coro/ValueOrFatal.h>
 #include <folly/coro/safe/SafeTask.h>
-#include <folly/coro/safe/detail/AsyncClosureBindings.h>
+#include <folly/coro/safe/detail/BindAsyncClosure.h>
 #include <folly/detail/tuple.h>
 
 #if FOLLY_HAS_IMMOVABLE_COROUTINES
@@ -48,12 +48,11 @@
 
 auto async_closure_make_cleanup_tuple(
     async_closure_private_t priv, auto&& arg, const exception_wrapper* err) {
-  // `co_cleanup` is allowed to return `Task<void>` or a tuple of them.
   auto to_lite_tuple = []<typename T>(T task) {
     static_assert(
-        noexcept_awaitable_v<T> && std::is_void_v<semi_await_result_t<T>>,
-        "`co_cleanup()` must return a `noexcept`-awaitable `void` coro. "
-        "Change your return type to `AsNoexcept<Task<>>` and don't throw.");
+        value_only_awaitable_v<T> && std::is_void_v<semi_await_result_t<T>>,
+        "`co_cleanup()` must return a value-only awaitable `void` coro. "
+        "Change your return type to `value_or_fatal<Task<>>` and don't throw.");
     return lite_tuple::tuple{std::move(task)};
   };
   if constexpr (has_async_object_private_hack_co_cleanup<decltype(arg)>) {
@@ -109,7 +108,7 @@
             // "unsafe" types that don't expose the inner type's `safe_alias`.
             std::move(inner_mover),
             std::move(storage_ptr),
-            // We don't require a `SafeTask` for `co_cleanup` because the coro
+            // We don't require a `safe_task` for `co_cleanup` because the coro
             // cannot outlive the object (or `exception_ptr*`) it references.
             manual_safe_val(std::move(reversed_noexcept_cleanups))...);
       },
@@ -135,9 +134,10 @@
     bool SetCancelTok,
     typename ResultT,
     safe_alias OuterSafety,
-    // This coro is noexcept-awaitable iff `async_closure_outer_coro_result` is
-    // `noexcept`.  But we don't want to restrict it for coros that are not
-    // marked `AsNoexcept` -- this boolean toggles its "is noexcept" asserts.
+    // This coro is value-only awaitable iff `async_closure_outer_coro_result`
+    // is `noexcept`. But we don't want to restrict it for coros that are not
+    // marked `value_or_fatal` -- this boolean toggles its "is noexcept"
+    // asserts.
     bool AssertNoexcept,
     typename OuterResT =
         drop_unit_t<decltype(async_closure_outer_coro_result<AssertNoexcept>(
@@ -145,8 +145,8 @@
             std::declval<lift_unit_t<ResultT>&&>()))>>
 std::conditional_t<
     OuterSafety >= safe_alias::closure_min_arg_safety,
-    SafeTask<OuterSafety, OuterResT>,
-    NowTask<OuterResT>>
+    safe_task<OuterSafety, OuterResT>,
+    now_task<OuterResT>>
 async_closure_outer_coro(
     async_closure_private_t priv,
     auto inner_mover,
@@ -158,7 +158,7 @@
   }
 
   // Pass our cancellation token to args that want it for cleanup.  The user
-  // code can throw -- e.g. `CancellationToken::merge()` may allocate.
+  // code can throw -- e.g. `cancellation_token_merge()` may allocate.
   if constexpr (SetCancelTok) {
     const auto& ctok = co_await co_current_cancellation_token;
     inner_err = try_and_catch([&]() {
@@ -219,11 +219,11 @@
 //
 // Closure safety checks follow the model of `SafeTask.h` -- and actually
 // reuse most of that implementation by requiring the inner coro to be a
-// `SafeTask`.
+// `safe_task`.
 //
 // Note that we don't check whether the callable passed into `async_closure`
 // is stateless, and we don't need to -- it is executed eagerly, and may be
-// a coroutine wrapper.  The coro callable underlying the inner `SafeTask`
+// a coroutine wrapper.  The coro callable underlying the inner `safe_task`
 // will have been verified to be stateless.
 //
 // Future: An `AsyncGenerator` closure flavor is possible, just think about
@@ -249,7 +249,7 @@
   static constexpr bool has_safe_args =
       (OuterSafety >= safe_alias::closure_min_arg_safety);
 
-  // The reason we need `SafeTask` here is that it have already detected any
+  // The reason we need `safe_task` here is that it have already detected any
   // by-reference arguments (impossible to detect otherwise), stateful
   // coros, and unsafe return types.
   static constexpr bool is_inner_coro_safe =
@@ -267,12 +267,12 @@
     static_assert(
         has_safe_args,
         "Args passed into `async_closure()` must have `safe_alias_of` of at "
-        "least `shared_cleanup`. `NowTask` and `async_now_closure()` do not "
+        "least `shared_cleanup`. `now_task` and `async_now_closure()` do not "
         "have this constraint. To force a movable closure, use `manual_safe_*`,"
         " and comment with a proof of why your usage is memory-safe.");
     static_assert(
         is_inner_coro_safe,
-        "`async_closure` currently only supports `SafeTask` as the inner coro.");
+        "`async_closure` currently only supports `safe_task` as the inner coro.");
     return NoexceptWrap::wrap_with([&]() { return std::move(outer_mover_)(); });
   }
 };
@@ -324,9 +324,9 @@
 decltype(auto) async_closure_resolve_backref(
     capture_private_t priv, auto& tup, Arg&) {
   constexpr auto Tag = Arg::folly_bindings_identifier_tag;
-  // `AsyncClosureBindings.h` populates tags via `named_bind_info_tag_v`, which
+  // `BindAsyncClosure.h` populates tags via `named_bind_info_tag_v`, which
   // uses `no_tag_t` to mean "no tag was set" -- so you can't look it up.
-  static_assert(!std::is_same_v<folly::bindings::ext::no_tag_t, decltype(Tag)>);
+  static_assert(!std::is_same_v<folly::bind::ext::no_tag_t, decltype(Tag)>);
   // This will fail on missing, or ambiguous tags.
   using Entry = decltype(async_closure_backref_get<Tag>(FOLLY_DECLVAL(ArgMap)));
   static_assert(
@@ -344,9 +344,9 @@
   return capture<Source&>(priv, forward_bind_wrapper(target.get_lref()));
 }
 
-// Replace `"x"_id` backreferences in the args of `capture_in_place` and
-// `capture_in_place_with` with `capture<T&>` references to the corresponding
-// capture storage.
+// Replace `"x"_id` backreferences in the args of `bind::capture_in_place` and
+// `bind::capture_in_place_with` with `capture<T&>` references to the
+// corresponding capture storage.
 //
 // Backrefs may ONLY point to capture storage -- any args moved into the inner
 // coro are subject to unspecified destruction order, and so could not safely
@@ -361,14 +361,14 @@
 struct async_closure_backref_populator<
     ArgMap,
     ArgI,
-    bind_wrapper_t<folly::bindings::detail::in_place_args_maker<T, Args...>>> {
+    bind_wrapper_t<folly::bind::detail::in_place_args_maker<T, Args...>>> {
   using BindWrap =
-      bind_wrapper_t<folly::bindings::detail::in_place_args_maker<T, Args...>>;
+      bind_wrapper_t<folly::bind::detail::in_place_args_maker<T, Args...>>;
   auto operator()(capture_private_t priv, auto& tup, BindWrap&& bw) const {
     return lite_tuple::apply(
         [&](Args&&... args) {
           return unsafe_tuple_to_bind_wrapper(
-              folly::bindings::make_in_place_with([&]() {
+              bind::in_place_with([&]() {
                 return T{[&]() -> decltype(auto) {
                   if constexpr (requires(Args a) {
                                   a.folly_bindings_identifier_tag;
@@ -431,34 +431,41 @@
   lite_tuple::tuple<Ts...> storage_tuple_;
 };
 
-template <size_t StorageI, typename Bs>
+template <size_t StorageI, typename Bs, bool DerefResult = false>
 decltype(auto) async_closure_bind_inner_coro_arg(
     capture_private_t priv, Bs& bs, auto& storage_ptr) {
-  if constexpr (is_async_closure_outer_stored_arg<Bs>) {
-    // "own": arg was already moved into `storage_ptr`.
-    auto& storage_ref = get_from_storage_ptr<StorageI>(storage_ptr);
-    static_assert(
-        std::is_same_v<
-            typename Bs::storage_type,
-            std::remove_reference_t<decltype(storage_ref)>>);
-    // `SharedCleanupClosure=true` preserves the `after_cleanup_ref_` prefix of
-    // the storage type.
-    return storage_ref.template to_capture_ref</*shared*/ true>(priv);
-  } else if constexpr (
-      // "own": Move stored `as_capture()` into inner coro.
-      is_instantiation_of_v<async_closure_inner_stored_arg, Bs> ||
-      // `scheduleSelfClosure` / `scheduleScopeClosure` self-references.
-      is_instantiation_of_v<async_closure_scope_self_ref_hack, Bs>) {
-    return typename Bs::storage_type{priv, std::move(bs.bindWrapper_)};
-  } else if constexpr (is_any_capture<Bs>) {
-    // "pass": Move `capture<Ref>` into the inner coro.
-    static_assert(std::is_reference_v<typename Bs::capture_type>);
-    return std::move(bs);
-  } else { // "regular": Non-`capture` binding.
-    static_assert(is_instantiation_of_v<async_closure_regular_arg, Bs>);
-    // We don't inspect `storage_type` here -- `detail/AsyncClosureBindings.h`
-    // should have ensured that `bind_info_t` was in a default, no-op state.
-    return std::move(bs).bindWrapper_.what_to_bind();
+  auto fn = [&]() -> decltype(auto) {
+    if constexpr (is_async_closure_outer_stored_arg<Bs>) {
+      // "own": arg was already moved into `storage_ptr`.
+      auto& storage_ref = get_from_storage_ptr<StorageI>(storage_ptr);
+      static_assert(
+          std::is_same_v<
+              typename Bs::storage_type,
+              std::remove_reference_t<decltype(storage_ref)>>);
+      // `SharedCleanupClosure=true` preserves the `after_cleanup_ref_` prefix
+      // of the storage type.
+      return storage_ref.template to_capture_ref</*shared*/ true>(priv);
+    } else if constexpr (
+        // "own": Move stored `bind::capture()` into inner coro.
+        is_instantiation_of_v<async_closure_inner_stored_arg, Bs> ||
+        // `scheduleSelfClosure` / `scheduleScopeClosure` self-references.
+        is_instantiation_of_v<async_closure_scope_self_ref_hack, Bs>) {
+      return typename Bs::storage_type{priv, std::move(bs.bindWrapper_)};
+    } else if constexpr (is_any_capture<Bs>) {
+      // "pass": Move `capture<Ref>` into the inner coro.
+      static_assert(std::is_reference_v<typename Bs::capture_type>);
+      return std::move(bs);
+    } else { // "regular": Non-`capture` binding.
+      static_assert(is_instantiation_of_v<async_closure_regular_arg, Bs>);
+      // We don't inspect `storage_type` here -- `detail/BindAsyncClosure.h`
+      // should have ensured that `bind_info_t` was in a default, no-op state.
+      return std::move(bs).bindWrapper_.what_to_bind();
+    }
+  };
+  if constexpr (DerefResult) {
+    return *fn();
+  } else {
+    return fn();
   }
 }
 
@@ -467,33 +474,12 @@
   T value;
 };
 
-// Eagerly construct -- but do not await -- an `async_closure`:
-//   - Resolve bindings.
-//   - Construct & store args for the user-supplied inner coro.
-//   - For ensuring cleanup in the face of `bad_alloc`, pre-allocate the
-//     outer task & `co_cleanup` tasks, if needed.
-//   - Create the inner coro, passing it `capture` references, or -- if
-//     there are no `co_cleanup` args and no outer coro -- quack-alike
-//     owning wrappers.
-//   - Marks the final user-facing task with the `safe_alias` that
-//     describes the memory-safety of the closure's arguments.
-//   - Returns the task inside a wrapper that statically checks the memory
-//     safety of the return & `make_inner_coro` types when
-//     `release_outer_coro()` is called.
-//
-// NB: Due to the "omit outer coro" optimization, `release_outer_coro()`
-// will in some cases return a no-overhead wrapper around the coro returned
-// by `make_inner_coro()`.
-//
-// Rationale: "Eager" is the only option matching user expectations, since
-// regular coroutine args are bound eagerly too.  Implementation-wise, all
-// `lang/Bindings.h` logic has to be resolved within the current statement,
-// since the auxiliary reference-bearing objects aren't valid beyond that.
-template <auto Cfg>
-auto bind_captures_to_closure(auto&& make_inner_coro, auto safeties_and_binds) {
-  auto& [arg_safeties, b_tup] = safeties_and_binds;
-
-  using BTupIs = std::make_index_sequence<std::tuple_size_v<decltype(b_tup)>>;
+template <bool OnlyGetInnerCoroType, auto Cfg, typename BTup>
+auto async_closure_inner_coro_and_storage(auto&& make_inner_coro, BTup& b_tup) {
+  // Ensure the compiler doesn't waste cycles on the "coro type detection"
+  // code-path for safe `async_closure` -- only `async_now_closure` needs it.
+  static_assert(Cfg.emit_now_task || !OnlyGetInnerCoroType);
+  using BTupIs = std::make_index_sequence<std::tuple_size_v<BTup>>;
   // For stored arg  @ `i`, `VtagStorageIs[i]` is a `*storage_ptr` index.
   using VtagStorageIs = decltype(lite_tuple::apply(
       [&]<typename... Bs>(Bs&...) {
@@ -541,68 +527,161 @@
         }(lite_tuple::get<ArgIs>(tup))...);
       }(b_tup, BTupIs{}, VtagStorageIs{}));
 
-  auto raw_inner_coro = lite_tuple::apply(
-      [&]<typename... Bs>(Bs&... bs) {
-        return [&]<size_t... ArgIs, size_t... StorageIs>(
-                   std::index_sequence<ArgIs...>, vtag_t<StorageIs...>) {
-          return make_inner_coro(
-              // Unpack `Bs`, `ArgIs`, and `StorageIs` jointly
-              [&]() -> decltype(auto) {
-                if constexpr (Cfg.is_invoke_member && ArgIs == 0) {
-                  // We have a `FOLLY_INVOKE_MEMBER`.  It accesses the
-                  // member function via `.`, but this arg is expected to be
-                  // `co_cleanup_capture<>` or `AsyncObjectPtr<>`, so we
-                  // "magically" dereference it here.
-                  //
-                  // On safety: Below, we assert that it it made a
-                  // `MemberTask<T>`, which `inner_rewrapped` will
-                  // implicitly unwrap & mark with a higher safety level.
-                  // `MemberTask` provides only a minimal safety
-                  // attestation, namely (besides arg 1, the implicit object
-                  // param), none of its args are taken by-reference.  This
-                  // is fine, since for `OuterSafety`, we will have
-                  // accounted for all the args' safety levels.
-                  return *async_closure_bind_inner_coro_arg<StorageIs, Bs>(
-                      capture_private_t{}, bs, storage_ptr);
-                } else {
-                  return async_closure_bind_inner_coro_arg<StorageIs, Bs>(
-                      capture_private_t{}, bs, storage_ptr);
-                }
-              }()...);
-        }(BTupIs{}, VtagStorageIs{}); // `StorageIs` indexes into `storage_ptr`
-      },
-      b_tup);
+  using StoragePtr = decltype(storage_ptr);
+  // The `apply` + nested lambdas jointly iterate over several packs:
+  //   - Binding tuple elements: `Bs... bs` from `b_tup`, indexed by `ArgIs...`
+  //   - `StorageIs...` point into `storage_ptr` for owned captures.
+  //
+  // (1) The return type depends on `OnlyGetInnerCoroType`.
+  //
+  // `true`: Only used inside a `decltype()`, which drives the logic from
+  // `async_closure_safeties_and_bindings` labeled `task_forces_shared_cleanup`.
+  // The return type is `tuple<type_identity<InnerCoro>, unused storage_ptr>`.
+  //
+  // `false`: This is the evaluated path that makes the actual inner coro.
+  //
+  // (2) If `DerefResult` is `true` below, this means the callable is a
+  // `FOLLY_INVOKE_MEMBER`.  It accesses the member function via `.`, but this
+  // arg is expected to be `co_cleanup_capture<>` or `AsyncObjectPtr<>`, so we
+  // "magically" dereference it here.
+  //
+  // On member-invocation safety: `bind_captures_to_closure` will assert that
+  // we made a `member_task<T>`, which `inner_rewrapped` will implicitly unwrap
+  // & mark with a higher safety level.  By itself, `member_task` provides only
+  // a minimal safety attestation, namely:
+  //   - For the implicit object param `Arg1`,
+  //       strict_safe_alias_of_v<Arg1> > shared_cleanup
+  //   - None of the other args are taken by-reference.
+  // This is fine, since for `OuterSafety`, we will have accounted for all the
+  // args' safety levels.
+  return lite_tuple::tuple{
+      lite_tuple::apply(
+          [&]<typename... Bs>(Bs&... bs) {
+            return [&]<size_t... ArgIs, size_t... StorageIs>(
+                       std::index_sequence<ArgIs...>, vtag_t<StorageIs...>) {
+              if constexpr (OnlyGetInnerCoroType) { // Non-evaluated, see (1)
+                return type_identity<detected_or_t<
+                    void*,
+                    invoke_result_t,
+                    decltype(make_inner_coro),
+                    decltype(async_closure_bind_inner_coro_arg<
+                            StorageIs,
+                            Bs,
+                            /*DerefResult*/ Cfg.is_invoke_member && ArgIs == 0>(
+                            capture_private_t{},
+                            FOLLY_DECLVAL(Bs&),
+                            FOLLY_DECLVAL(StoragePtr&)))...>>{};
+              } else { // Actually create a coro
+                return make_inner_coro(
+                    async_closure_bind_inner_coro_arg<
+                        StorageIs,
+                        Bs,
+                        /*DerefResult*/ (Cfg.is_invoke_member && ArgIs == 0)>(
+                        capture_private_t{}, bs, storage_ptr)...);
+              }
+            }(BTupIs{}, VtagStorageIs{});
+          },
+          b_tup),
+      std::move(storage_ptr)};
+}
 
-  // First, unwrap `AsNoexcept` so that `safe_task_traits` below can work.
-  // We only allow `AsNoexcept` as the outer wrapper.
-  using NoexceptWrap = as_noexcept_rewrapper<decltype(raw_inner_coro)>;
+// Eagerly construct -- but do not await -- an `async_closure`:
+//   - Resolve bindings.  For `emit_now_closure` this can  involve a tricky
+//     dance with trying two different versions of bindings, as described
+//     inside `async_closure_safeties_and_bindings`.
+//   - Construct & store args for the user-supplied inner coro.
+//   - For ensuring cleanup in the face of `bad_alloc`, pre-allocate the
+//     outer task & `co_cleanup` tasks, if needed.
+//   - Create the inner coro, passing it `capture` references, or -- if
+//     there are no `co_cleanup` args and no outer coro -- quack-alike
+//     owning wrappers.
+//   - Marks the final user-facing task with the `safe_alias` that
+//     describes the memory-safety of the closure's arguments.
+//   - Returns the task inside a wrapper that statically checks the memory
+//     safety of the return & `make_inner_coro` types when
+//     `release_outer_coro()` is called.
+//
+// NB: Due to the "omit outer coro" optimization, `release_outer_coro()`
+// will in some cases return a no-overhead wrapper around the coro returned
+// by `make_inner_coro()`.
+//
+// Rationale: "Eager" is the only option matching user expectations, since
+// regular coroutine args are bound eagerly too.  Implementation-wise, all
+// `lang/bind/Bind.h` logic has to be resolved within the current statement,
+// since the auxiliary reference-bearing objects aren't valid beyond that.
+template <auto Cfg>
+auto bind_captures_to_closure(auto&& make_inner_coro, auto safeties_and_binds) {
+  // The comment in `async_closure_safeties_and_bindings` covers WHY there are
+  // two `b_tup` flavors, and why the overall algorithm is correct. In brief:
+  //   - `b_tup` is the happy path.
+  //   - `b_tup_for_unsafe_task` is only used when it turns out that the inner
+  //     coro for an `async_now_closure` is an unsafe task.  It is `nullptr` on
+  //     code paths where it definitely won't get used.
+  auto& [arg_min_safety, b_tup, b_tup_for_unsafe_task] = safeties_and_binds;
+  auto pick_safest_b_tup = [&]() -> auto& {
+    // Avoid the compile-time cost of `unsafe_inner_coro_forces_shared_cleanup`
+    if constexpr (
+        !Cfg.emit_now_task ||
+        decltype(arg_min_safety)::args_force_shared_cleanup) {
+      return b_tup;
+    } else {
+      // Per the discussion in `async_closure_safeties_and_bindings`, if
+      // `async_now_closure` produces a `safe_task`, then we don't have to
+      // force shared-cleanup behavior, and may use the `b_tup` bindings.
+      // Using `b_tup_for_unsafe_task` will cause the inner coro to see
+      // capture-refs of lower safety (e.g.  `after_cleanup_ref` instead of
+      // `co_cleanup_safe_ref`).
+      constexpr bool unsafe_inner_coro_forces_shared_cleanup =
+          Cfg.emit_now_task &&
+          (strict_safe_alias_of_v<typename std::tuple_element_t<
+               0,
+               decltype(async_closure_inner_coro_and_storage<
+                        /*get type*/ true,
+                        Cfg>(
+                   static_cast<decltype(make_inner_coro)>(make_inner_coro),
+                   b_tup))>::type> < safe_alias::unsafe_closure_internal);
+      if constexpr (unsafe_inner_coro_forces_shared_cleanup) {
+        return b_tup_for_unsafe_task;
+      } else { // Stateless inner coro
+        return b_tup;
+      }
+    }
+  };
+  auto [raw_inner_coro, storage_ptr] =
+      async_closure_inner_coro_and_storage</*get type*/ false, Cfg>(
+          static_cast<decltype(make_inner_coro)>(make_inner_coro),
+          pick_safest_b_tup());
+
+  // First, unwrap `value_or_fatal` so that `safe_task_traits` below can work.
+  // We only allow `value_or_fatal` as the outer wrapper.
+  using NoexceptWrap = value_or_fatal_rewrapper<decltype(raw_inner_coro)>;
   auto unwrapped_inner = []<typename T>(T&& t) {
-    if constexpr (NoexceptWrap::as_noexcept_wrapped) {
+    if constexpr (NoexceptWrap::value_or_fatal_wrapped) {
       return NoexceptWrap::unwrapTask(std::move(t));
     } else {
-      return mustAwaitImmediatelyUnsafeMover(std::move(t))();
+      return ::folly::ext::must_use_immediately_unsafe_mover(std::move(t))();
     }
   }(std::move(raw_inner_coro));
 
   // Compute the safety of the arguments being passed by the caller.
-  constexpr safe_alias OuterSafety = Cfg.force_shared_cleanup // making NowTask
+  constexpr safe_alias OuterSafety = Cfg.emit_now_task
       ? safe_alias::unsafe
-      : vtag_least_safe_alias(decltype(arg_safeties){});
+      : decltype(arg_min_safety)::parent_view;
   // Also check that the coroutine function's signature looks safe.
   constexpr safe_alias InnerSafety =
       safe_task_traits<decltype(unwrapped_inner)>::arg_safety;
 
   // This converts `raw_inner_task` into a "task mover" that can be plumbed
   // down to, and used by, `async_closure_outer_coro()`.  We do 3 tricks here:
-  //   - Wrap all tasks into a "mover" to handle immovables like `NowTask`.
-  //   - For `ClosureTask`, we'll internally LIE about its safety to let it be
+  //   - Wrap all tasks into a "mover" to handle immovables like `now_task`.
+  //   - For `closure_task`, we'll internally LIE about its safety to let it be
   //     `co_await`ed. Per below, that's OK thanks to `async_closure_wrap_coro`.
-  //   - For `SafeTask` closures with the "no outer coro" optimization, we set
+  //   - For `safe_task` closures with the "no outer coro" optimization, we set
   //     the inner coro's safety to `OuterSafety`, for reasons explained below.
   auto inner_mover = [&]() {
     // The first branch is always taken for safe/movable `async_closure()`
     // invocations.  For `async_now_closure()`, this branch is taken iff the
-    // inner coro is a `ClosureTask` or other `SafeTask`.
+    // inner coro is a `closure_task` or other `safe_task`.
     if constexpr (InnerSafety >= safe_alias::unsafe_closure_internal) {
       // In the presence of stored `capture`s, `InnerSafety` (as measured by
       // `safe_alias_of` on the inner coro) is not what we want.  That's
@@ -610,15 +689,16 @@
       // to discourage them being moved out of the closure.  Instead, we set
       // safety based on `vtag_safety_of_async_closure_args` (`OuterSafety`).
       //
-      // `ClosureTask` cannot be `co_await`ed, so clip to `>= min_arg_safety`.
+      // `closure_task` cannot be `co_await`ed, so clip to `>= min_arg_safety`.
       // This is OK since `async_closure_wrap_coro` will later enforce:
       //   OuterSafety >= closure_min_arg_safety
       constexpr auto newSafety =
           std::max(OuterSafety, safe_alias::closure_min_arg_safety);
-      return mustAwaitImmediatelyUnsafeMover(
+      return ::folly::ext::must_use_immediately_unsafe_mover(
           std::move(unwrapped_inner).template withNewSafety<newSafety>());
     } else { // The "new safety" rewrite doesn't apply to unsafe tasks!
-      return mustAwaitImmediatelyUnsafeMover(std::move(unwrapped_inner));
+      return ::folly::ext::must_use_immediately_unsafe_mover(
+          std::move(unwrapped_inner));
     }
   }();
 
@@ -630,9 +710,9 @@
   // would not be true e.g.  if the user passed something like this:
   //   [](int num, auto me) { return me->addNumber(num); }
   static_assert(
-      std::is_same_v<MemberTask<ResultT>, decltype(unwrapped_inner)> ==
+      std::is_same_v<member_task<ResultT>, decltype(unwrapped_inner)> ==
           Cfg.is_invoke_member,
-      "To use `MemberTask<>` coros with `async_closure`, you must pass "
+      "To use `member_task<>` coros with `async_closure`, you must pass "
       "the callable as `FOLLY_INVOKE_MEMBER(memberName)`, and pass the "
       "instance's `capture`/`AsyncObjectPtr`/... as the first argument.");
 
@@ -644,21 +724,21 @@
           "Cannot `co_return *after_cleanup()` without a cleanup arg");
       return std::move(inner_mover);
     } else {
-      return mustAwaitImmediatelyUnsafeMover(
+      return ::folly::ext::must_use_immediately_unsafe_mover(
           async_closure_make_outer_coro<
               /*cancelTok*/ true,
               ResultT,
               OuterSafety,
-              NoexceptWrap::as_noexcept_wrapped>(
+              NoexceptWrap::value_or_fatal_wrapped>(
               async_closure_private_t{},
               std::move(inner_mover),
               std::move(storage_ptr)));
     }
   }();
 
-  if constexpr (Cfg.force_shared_cleanup) {
+  if constexpr (Cfg.emit_now_task) {
     return NoexceptWrap::wrap_with([&]() {
-      return toNowTask(std::move(outer_mover)());
+      return to_now_task(std::move(outer_mover)());
     });
   } else {
     return async_closure_wrap_coro<
diff --git a/folly/folly/coro/safe/detail/AsyncClosureBindings.h b/folly/folly/coro/safe/detail/AsyncClosureBindings.h
deleted file mode 100644
--- a/folly/folly/coro/safe/detail/AsyncClosureBindings.h
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <compare>
-
-#include <folly/coro/safe/Captures.h>
-#include <folly/coro/safe/SafeAlias.h>
-
-/// This header's `async_closure_safeties_and_bindings` implements the
-/// argument-binding logic for `async_closure`.
-///
-/// Before reading further, make sure to get familiar with:
-///   - `folly/lang/Bindings.md` for `bound_args` & friends.
-///   - `docs/Captures.md` to understand the `capture` type wrappers, and the
-///     safety upgrade/downgrade rules for passing them into closures.
-/// In particular, know this distinction:
-///   * "owned captures" look like `capture<V>`.  These are wrappers tied
-///     to the closure whose `as_capture()` created it. Note that a closure
-///     with an outer coro will pass these as `capture<V&>` to the inner task.
-///   * "capture references" are `capture<V&>` or `<V&&>, implicitly created
-///     for the closure from any caller-provided `capture` (value or ref).
-///
-/// `async_closure` takes user-specified closure arguments as `bound_args`, an
-/// immovable object with 1-expression lifetime.  This header plumbs them
-/// through some transformations.  In opt builds, it is intended to be elided
-/// by compiler's alias analysis (NB: this needs benchmarks & perhaps tweaks).
-///
-/// Every variant of `async_closure` invokes `..._safeties_and_bindings()`,
-/// which does several jobs.  Here's a summary of the data flow:
-///   * Figure out if the closure needs an outer coro, or if it can be elided.
-///   * Measure the safety of the closure, as seen by its caller.  This uses
-///     the safeties of the arguments **before** any transformations.
-///   * Transform the arg tuple:
-///       - Convert each entry of the `bound_args` into a `capture` ref (when
-///         the caller gave us a `capture`), or one of 4 tag types
-///         (`async_closure*_arg` or `async_closure*_self_ref_hack`).  The tag
-///         types tells the `async_closure` implementation whether to store the
-///         arg, and how to bind it to the inner closure.
-///       - Figure out the storage type for each `as_capture` binding using
-///         `folly::bindings::binding_policy`, to support `make_in_place*`.
-///       - Transform non-owned `capture`s via `to_capture_ref`.  Parents'
-///         owned captures are implicitly passed by-ref; `after_cleanup_` refs
-///         are "upgraded" if possible.  Docs in `Captures.md`.
-///       - Apply special handling to the first argument when the closure runs
-///         `FOLLY_INVOKE_MEMBER`.
-///       - Other args are perfectly forwarded.
-///   * Validate the user inputs and try to issue readable error messages.
-///     Also check internal invariants.
-///
-/// After the validation & transformation, `detail/AsyncClosure.h` is
-/// responsible for actually storing the args, and creating the coroutine.
-///
-/// ## Implementation glossary
-///
-/// This header classifies the bound arguments into a few categories:
-///   * "owned capture" or just "own": Make a new `capture` whose storage (and
-///     cleanup) belongs to this closure.
-///       - These correspond to `async_closure_{inner,outer}_stored_arg`.
-///       - If the closure is "shared cleanup", the safety of the new capture
-///         is downgraded to `after_cleanup_ref`.
-///       - The inner task sees a `capture` ref for closures with an outer
-///         coro, and a `capture` value otherwise.
-///   * "pass capture ref" or just "pass": A `capture` from the caller.
-///       - These are passed as `capture<Ref>`, even if the input is a value.
-///       - The inner coro may see upgraded safety relative to the caller.
-///   * "regular arg": The easy / normal case -- simply bind a forwarding
-///     reference from the caller to the inner coro.  The reference is dressed
-///     in `async_closure_regular_arg`.
-///   * "self-reference hack": See `async_closure_scope_self_ref_hack`.
-
-#if FOLLY_HAS_IMMOVABLE_COROUTINES
-FOLLY_PUSH_WARNING
-FOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS
-
-namespace folly::coro {
-class AsyncObject;
-class AsyncScopeSlotObject;
-template <typename, size_t>
-class SafeAsyncScopeContextProxy;
-template <typename>
-class AsyncObjectNonSlotPtr;
-} // namespace folly::coro
-
-namespace folly::coro::detail {
-
-template <safe_alias... Vs>
-constexpr safe_alias vtag_least_safe_alias(vtag_t<Vs...>) {
-  return std::min({safe_alias::maybe_value, Vs...});
-}
-
-//
-// There are 4 tag types here, which all quack the same interface:
-//   * `storage_type`: For storing "owned captures", but also for measuring
-//     "caller's point-of-view" safety of regular args.
-//   * `bindWrapper_`: A `bind_wrapper_t<T>`, which is literally just `T` but
-//     preserving the value category even for references.  It has either the
-//     forwarding reference or the value being bound.  Note that `T` is not
-//     necessarily related to `storage_type` -- for in-place construction, it
-//     is a "maker", which is implicitly-convertible to the `storage_type`.
-//
-// There's also 5th case without a tag type -- when passing capture refs,
-// `async_closure_safeties_and_bindings()` simply emits an unwrapped `capture`,
-// and `AsyncClosure.h` detects it via `is_any_capture<Bs>`.
-//
-// The goal is that none of the 5 cases instantiate any storage, or call
-// copy/move constructors until the final moment, when we either:
-//   - pass the arg to the inner coro, or
-//   - store it in the outer coro's `unique_ptr<tuple<>>` of owned captures.
-//
-
-// This is just a fancy forwarding reference, never a value.
-template <typename Storage, typename BindWrapper>
-struct async_closure_regular_arg {
-  using storage_type = Storage;
-  BindWrapper bindWrapper_;
-};
-
-// Use `is_base_of` since `instantiation_of` cannot handle no-type templates
-class async_closure_outer_stored_arg_base {};
-template <typename T>
-concept is_async_closure_outer_stored_arg =
-    std::is_base_of_v<async_closure_outer_stored_arg_base, T>;
-
-// For a given closure, all `_stored_arg` tags are going to be of one flavor.
-// With an outer coro, we capture info required to resolve backrefs.  Also, the
-// "has outer coro" decision isn't exported in any way besides the "inner" vs
-// "outer" stored arg type.  If useful, we could easily refactor this to be one
-// `_owned_capture` tag type, and branch on `has_outer_coro` in
-// `AsyncClosure.h`.  Reworking things this way would make it possible to have
-// an outer coro without storage, which might be needed if someone has a
-// legitimate use-case for captures that define `setParentCancelToken()` or
-// `setParentExecutor()` without defining `co_cleanup()` (dubious!).
-template <is_any_capture Storage, typename BindWrapper, size_t ArgI, auto Tag>
-struct async_closure_outer_stored_arg : async_closure_outer_stored_arg_base {
-  using storage_type = Storage;
-  constexpr static inline size_t arg_idx = ArgI;
-  constexpr static inline auto tag = Tag;
-  BindWrapper bindWrapper_;
-};
-template <is_any_capture Storage, typename BindWrapper>
-struct async_closure_inner_stored_arg {
-  using storage_type = Storage;
-  BindWrapper bindWrapper_;
-};
-
-// ## Why does this `self_ref_hack` type even exist?
-//
-// To avoid synchronization costs, `folly::coro` async scopes disallow `add()`
-// after `joinAsync()` has completed (violating this is UB).  However, it is
-// always safe to call `add()` on an async scope from a task running on that
-// **same** scope, even if `joinAsync()` has already started. This is because
-// any active scope awaitable prevents its `joinAsync()` from completing.
-//
-// Unfortunately, though it would be safe, you cannot directly pass an
-// `async_arg_cleanup<Scope&>` ref (`shared_cleanup` safety) into a closure
-// being scheduled on the SAME async scope (needs `>= co_cleanup_safe_ref`).
-// That's because at compile-time, we cannot tell if the "scope being scheduled
-// on" is the same as "the scope being referenced".
-//
-// Moreover, "pass a ref to the current scope" is the ONLY case that is always
-// safe [1].  Otherwise, the closure with the scope ref could run `add()` when
-// the (other) scope that it references had already been cleaned up.
-//
-// [1] Future: Thanks to `co_cleanup` ordering, it may later be feasible to
-// allow scopes that are passed later (cleaned up earlier) to reference those
-// that are passed earlier (cleaned up later).
-//
-// ## How does `self_ref_hack` help allow safe self-references for scopes?
-//
-// The solution is to introduce `scheduleScopeClosure()`, which acts just like
-// `schedule(async_closure())`, but prepends an "implicit scope parameter" to
-// the user-supplied `bound_args{}`.
-//
-// This implicit param is `self_ref_hack`, which is handled specially inside
-// `detail/AsyncClosure*`.  As a result, the user's inner task gets a
-// `co_cleanup_capture<Scope&>` as its first argument.
-//
-// Analogously, `AsyncObject::scheduleSelfClosure()` uses this mechanism to
-// allow sub-closures to safely reference the `Slot` of the object, which
-// contains the "current" async scope.
-//
-// ## Why are all aspects of `self_ref_hack` protected?
-//
-// This is NOT safe to use in other settings, because:
-// (1) We instantiates a new `capture` ref from a bare reference, bypassing the
-//     usual lifetime safety checks.
-// (2) This `ref_hack` object is deliberately excluded from the final
-//     `async_closure`'s safety accounting -- it only affects the
-//     `shard_cleanup` downgrade.
-// Both of these are ONLY okay because the sub-closure's own scope outlives it.
-template <typename Storage, typename BindWrapper>
-struct async_closure_scope_self_ref_hack {
-  using storage_type = Storage;
-
- protected:
-  template <size_t, typename Bs>
-  friend decltype(auto) async_closure_bind_inner_coro_arg(
-      capture_private_t, Bs&, auto&);
-  template <typename, size_t>
-  friend class folly::coro::SafeAsyncScopeContextProxy;
-  friend class folly::coro::AsyncScopeSlotObject;
-  // The innards are `protected`, since `transform_binding` lets this be
-  // supplied from outside the closure machinery.  Any new client being
-  // added here must think THOROUGHLY about the risks in the class docblock!
-  explicit async_closure_scope_self_ref_hack(BindWrapper b)
-      : bindWrapper_(std::move(b)) {}
-  BindWrapper bindWrapper_;
-};
-
-struct binding_helper_cfg {
-  bool is_shared_cleanup_closure;
-  bool has_outer_coro;
-  bool in_safety_measurement_pass;
-  constexpr auto operator<=>(const binding_helper_cfg&) const = default;
-};
-
-template <typename Binding, auto Cfg, size_t ArgI>
-class capture_binding_helper;
-
-struct capture_ref_measurement_stub {};
-
-// This helper class only has static members.  It exists only so that the
-// various functions can share some type aliases.
-template <
-    std::derived_from<folly::bindings::ext::bind_info_t> auto BI,
-    typename BindingType,
-    auto Cfg,
-    size_t ArgI>
-class capture_binding_helper<
-    folly::bindings::ext::binding_t<BI, BindingType>,
-    Cfg,
-    ArgI> {
- private:
-  // A constraint on the template would make forward-declarations messy.
-  static_assert(std::is_same_v<decltype(Cfg), binding_helper_cfg>);
-
-  using category_t = folly::bindings::ext::category_t;
-  using ST = typename folly::bindings::ext::binding_policy<
-      folly::bindings::ext::binding_t<BI, BindingType>>::storage_type;
-  using UncvrefST = std::remove_cvref_t<ST>;
-
-  // "Pass capture ref" validation.  Here, `ST` is either a value or a
-  // reference `capture`.  `RetST` is the `capture<Ref>` for the inner task.
-  //
-  // There is no "business logic" here.  This only documents the possible data
-  // flows, `static_assert`s a few common user errors for better compiler
-  // messages, and refers to the relevant unit tests.
-  template <typename RetST>
-  static constexpr void static_assert_passing_capture() {
-    using ArgT = typename UncvrefST::capture_type;
-    static_assert(std::is_reference_v<ST> == (BI.category == category_t::ref));
-    if constexpr (std::is_reference_v<ArgT>) { // Is `capture<Ref>`?
-      // Design note: Why do we automatically pass all `capture`s by-reference?
-      // As an alternative, recall that `folly::bindings` has `const_ref` /
-      // `mut_ref`.  These modifiers could be hijacked as a mandatory marking
-      // for `captures` that get passed by-reference.  That might seem more
-      // explicit, but also more confusing and harder to use:
-      //   - `const_ref` / `mut_ref CANNOT be used for "regular" args -- they're
-      //     by-reference iff the caller writes `T&` in the signature.
-      //   - `capture`s are intended to belong to the parent closure, it rarely
-      //     makes sense to copy or move them.
-      //   - Syntactically, `capture`s behave like pointers.
-      //   - If we needed an explicit `const_ref` / `mut_ref` only to pass
-      //     `capture<Val>` as a `capture<Ref>`, then migrating a closure from
-      //     "has outer coro" to "lacks outer coro" would require adding such a
-      //     modifier at every callsite.
-      //   - You can still move out the contents of a capture into a child by
-      //     passing an rvalue ref as `std::move(cap)` to the child, or by
-      //     passing the actual value via `*std::move(cap)`.  Similarly, `*cap`
-      //     would copy the value.
-      // N.B.  We DO use `as_capture{const_ref{}}` etc in order to convert
-      // plain references from a parent coro into `capture` refs in a child
-      // closure, see "capture-by-reference" in `Captures.md`.
-      static_assert(
-          !std::is_reference_v<ST>,
-          "Pass `capture<Ref>` by value, do not use `const_ref` / `mut_ref`");
-      // Passing the caller's `capture<Ref>` makes a new `capture` ref object.
-      if constexpr (std::is_lvalue_reference_v<BindingType>) {
-        // Improve errors over just "deleted copy ctor".
-        static_assert(
-            !std::is_rvalue_reference_v<ArgT>,
-            "capture<V&&> is move-only. Try std::move(yourRef).");
-
-        // `check_capture_lref_to_lref` tests this branch -- passing a
-        // `capture<Ref>` that the caller bound as an lvalue.  We'll copy it,
-        // potentially upgrading `after_cleanup_capture` -> `capture.
-      } else {
-        // Cleanup args don't support rval refs.  No user-facing message, since
-        // `co_cleanup_capture` would first have a constraint failure.
-        static_assert(!is_any_co_cleanup_capture<UncvrefST>);
-
-        // Tests: `check_capture_lref_to_rref` & `check_capture_rref_to_rref`.
-        // An input `capture<Ref>` bound as an rvalue gives the child a
-        // `capture<Val&&>`.
-        //
-        // It is in some sense optional to support this, since users can
-        // pass around lval refs and `std::move(*argRef)` at the last
-        // minute.  However, I wanted to encourage the best practice of
-        // `std::move(argRef)` at the outermost callsite that knows about
-        // the move.  Reasons:
-        //   - The initial `std::move(arg)` enables use-after-move linting
-        //     in the outermost scope.
-        //   - `capture<T&&>` is move-only, meaning subsequent scopes also
-        //     get use-after-move linting.
-        //   - Future: we could build a debug-only use-after-move runtime
-        //     checker by adding some state on `capture`s.
-      }
-    } else { // Is `capture<Val>`?
-      // Cleanup args require an outer coro, so it should never be the case
-      // that `*co_cleanup_capture<Val>` is being passed to a child..
-      static_assert(!is_any_co_cleanup_capture<UncvrefST>);
-
-      // Tested in `check_capture_val_to_ref`: `capture<Val>` is implicitly
-      // passed as `capture<Ref>`.
-    }
-  }
-
-  template <typename T>
-  static constexpr auto store_as(auto bind_wrapper) {
-    if constexpr (Cfg.has_outer_coro) {
-      return async_closure_outer_stored_arg<
-          T,
-          decltype(bind_wrapper),
-          ArgI,
-          folly::bindings::ext::named_bind_info_tag_v<decltype(BI)>>{
-          .bindWrapper_ = std::move(bind_wrapper)};
-    } else {
-      return async_closure_inner_stored_arg<T, decltype(bind_wrapper)>{
-          .bindWrapper_ = std::move(bind_wrapper)};
-    }
-  }
-
-  // "owned capture": The closure creates storage for `as_capture()` bindings
-  static constexpr auto store_capture_binding(auto bind_wrapper) {
-    static_assert(
-        !is_any_capture<ST>,
-        "Given a capture `c`, do not write `as_capture(c)` to pass it to a "
-        "closure. Just write `c` as the argument, and it'll automatically "
-        "be passed as a capture reference.");
-    if constexpr (has_async_closure_co_cleanup<ST>) {
-      static_assert(Cfg.has_outer_coro);
-      // Future: Add a toggle to emit `restricted_co_cleanup_capture`
-      return store_as<co_cleanup_capture<ST>>(std::move(bind_wrapper));
-    } else if constexpr (BI.captureKind_ == capture_kind::indirect) {
-      if constexpr (Cfg.is_shared_cleanup_closure) {
-        return store_as<after_cleanup_capture_indirect<ST>>(
-            std::move(bind_wrapper));
-      } else {
-        return store_as<capture_indirect<ST>>(std::move(bind_wrapper));
-      }
-    } else if constexpr (
-        !Cfg.has_outer_coro &&
-        // `make_in_place*` is often used for immovable types, so without an
-        // outer coro, they must be on-heap to pass ownership to the inner coro.
-        folly::bindings::ext::is_binding_t_type_in_place<BindingType> &&
-        // Heuristic: Moving a type is usually cheaper than putting it on
-        // the heap.  If not, people can always use `capture_indirect` with
-        // `unique_ptr`...  Or, we could later add new capture kinds, like
-        // `plain_auto_storage = 0`, `plain_heap`, and `plain_non_heap`.
-        !std::is_move_constructible_v<BindingType>) {
-      if constexpr (Cfg.is_shared_cleanup_closure) {
-        return store_as<after_cleanup_capture_heap<ST>>(
-            std::move(bind_wrapper));
-      } else {
-        return store_as<capture_heap<ST>>(std::move(bind_wrapper));
-      }
-    } else {
-      if constexpr (Cfg.is_shared_cleanup_closure) {
-        return store_as<after_cleanup_capture<ST>>(std::move(bind_wrapper));
-      } else {
-        return store_as<capture<ST>>(std::move(bind_wrapper));
-      }
-    }
-  }
-
-  template <typename>
-  static inline constexpr bool is_supported_capture_bind_info_v = false;
-
-  template <>
-  static inline constexpr bool
-      is_supported_capture_bind_info_v<capture_bind_info_t> = true;
-
-  // Future: Right now, we only check that `"x"_id = ` tags are unique at time
-  // of use, and this only applies for stored captures.  But, from a pure "code
-  // quality" point of view, it would be reasonable to demand that all tags are
-  // unique, and that they are all used.  This could be done either as a linter
-  // or in this file, at some compile-time cost.
-  template <auto Tag>
-  static inline constexpr bool is_supported_capture_bind_info_v<
-      folly::bindings::ext::named_bind_info_t<Tag, capture_bind_info_t>> = true;
-
- public:
-  // Transforms the binding as per the file docblock, returns a new binding.
-  // (either one of the 4 tag types above, or `capture<Ref>`)
-  static constexpr auto transform_binding(auto bind_wrapper) {
-    if constexpr (is_supported_capture_bind_info_v<decltype(BI)>) {
-      // Implement "capture-by-reference", docs in `Captures.md`
-      if constexpr (BI.category == category_t::ref) {
-        // Test in `check_parent_capture_ref`
-        static_assert(std::is_reference_v<ST>);
-        static_assert(
-            !is_any_capture<UncvrefST>,
-            "Do not use `const_ref` / `mut_ref` verbs to pass a `capture` to "
-            "a child closure -- just pass it directly.");
-        // It should be hard to get a ref to a co_cleanup type
-        static_assert(!has_async_closure_co_cleanup<UncvrefST>);
-        if constexpr (Cfg.in_safety_measurement_pass) {
-          return capture_ref_measurement_stub{};
-        } else if constexpr (Cfg.is_shared_cleanup_closure) {
-          return after_cleanup_capture<ST>{
-              capture_private_t{}, std::move(bind_wrapper)};
-        } else {
-          return capture<ST>{capture_private_t{}, std::move(bind_wrapper)};
-        }
-      } else { // Tests in `check_stored_*`.
-        static_assert(!std::is_reference_v<ST>);
-        return store_capture_binding(std::move(bind_wrapper));
-      }
-    } else { // Bindings for arguments the closure does NOT store.
-      static_assert(
-          std::is_same_v<
-              vtag_t<BI>,
-              vtag_t<folly::bindings::ext::bind_info_t{}>>,
-          "`folly::bindings::` modifiers like `constant` (or `\"x\"_id = `) "
-          "only make sense with `as_capture()` bindings -- for example, to "
-          "move a mutable value into `const` capture storage. For regular "
-          "args, use `const` in the signature of your inner coro, and/or "
-          "`std::as_const` when passing the arg.");
-      // If we allowed `make_in_place` without `as_capture`, the argument would
-      // require a copy or a move to be passed to the inner task (which the
-      // type may not support).  If `as_capture` isn't appropriate, the user
-      // can also work around that via `std::make_unique<TheirType>` and/or
-      // `as_capture_indirect`.
-      static_assert(
-          !folly::bindings::ext::is_binding_t_type_in_place<BindingType>,
-          "Did you mean `capture_in_place<T>(...)`?");
-      if constexpr (is_any_capture<UncvrefST>) { // Tests in `check_capture_*`
-        // Pass preexisting `capture`s (NOT owned by this closure).
-        // Future: Add a toggle to make `restricted_co_cleanup_capture` refs.
-        auto arg_ref =
-            std::move(bind_wrapper)
-                .what_to_bind()
-                .template to_capture_ref<Cfg.is_shared_cleanup_closure>(
-                    capture_private_t{});
-        static_assert_passing_capture<decltype(arg_ref)>();
-        return std::move(arg_ref);
-      } else if constexpr (
-          is_instantiation_of_v<async_closure_scope_self_ref_hack, UncvrefST>) {
-        // This `ref_hack` type quacks like the `stored_arg` types, but we need
-        // to unwrap it for it to be handled correctly downstream.
-        return std::move(bind_wrapper).what_to_bind();
-      } else { // Test in `check_regular_args`
-        // "regular" args -- neither an owned capture (`as_capture()` et al),
-        // nor a parent's `capture`. Passed via forwarding reference.
-
-        // This may be redundant, since `co_cleanup_capture` enforces that
-        // cleanup types are immovable.  If we did allow passing bare
-        // `co_cleanup` types, it could violate memory safety protections for
-        // `async_closure`s.  For `async_now_closure`, there is also no obvious
-        // use-case for passing `*captureVar` by-reference into the child.
-        static_assert(
-            !has_async_closure_co_cleanup<UncvrefST>,
-            "This argument implements `async_closure` cleanup, so you should "
-            "almost certainly pass it `as_capture()` -- or, if you already "
-            "have as a reference `capture`, by-value.");
-
-        return async_closure_regular_arg<ST, decltype(bind_wrapper)>{
-            .bindWrapper_ = std::move(bind_wrapper)};
-      }
-    }
-  }
-};
-
-// See `vtag_safety_of_async_closure_args` for the docs.
-// NB: As a nested lambda, this breaks on clang-17 due to compiler bugs.
-template <bool ParentViewOfSafety, typename T>
-auto vtag_safety_of_async_closure_arg() {
-  // "owned capture": `store_as` outputs `async_closure_*_stored_arg`.
-  if constexpr (
-      is_async_closure_outer_stored_arg<T> ||
-      is_instantiation_of_v<async_closure_inner_stored_arg, T>) {
-    using CT = typename T::storage_type::capture_type;
-    static_assert(!std::is_reference_v<CT>);
-    // Stored captures are as safe as the type being stored.  For example, when
-    // a closure stores a `BackgroundTask<Safety, T>`, it cannot be safer than
-    // `Safety`.  We don't use `safe_alias_of_v` here because `AsyncObject.h`
-    // specializes `capture_safety_impl_v`.
-    return vtag<capture_safety_impl_v<CT>>;
-  } else if constexpr ( //
-      is_instantiation_of_v<async_closure_scope_self_ref_hack, T>) {
-    // This is a closure made by `spawn_self_closure()` et al. It must:
-    //  - Avoid marking the closure's outer task `shared_cleanup`, so it can
-    //    still be added to the scope that made it (`if` branch).
-    //  - Downgrade [*] the safety of its own captures (`else` branch).
-    //
-    // [*] It would be memory-unsafe to reference such captures from
-    // recursively scheduled closures on the same scope!
-    if constexpr (ParentViewOfSafety) {
-      return vtag<>;
-    } else {
-      constexpr auto storage_safety = safe_alias_of_v<typename T::storage_type>;
-      // In current usage, ref_hack can only contain `co_cleanup_capture<V&>`.
-      static_assert(storage_safety == safe_alias::shared_cleanup);
-      return vtag<storage_safety>;
-    }
-  } else if constexpr (is_any_capture<T>) {
-    // "pass capture ref": Output of the `to_capture_ref` branch.
-    static_assert(std::is_reference_v<typename T::capture_type>);
-    return vtag<safe_alias_of_v<T>>;
-  } else if constexpr (std::is_same_v<capture_ref_measurement_stub, T>) {
-    if constexpr (ParentViewOfSafety) {
-      // Only allow capture-by-reference in `async_now_closure`s
-      return vtag<safe_alias::unsafe>;
-    } else {
-      // But, don't do closure-internal downgrades, since `transform_bindings`
-      // tries to prevent it from taking in refs to co_cleanup types this way.
-      return vtag<>;
-    }
-  } else {
-    // "regular arg": A non-`capture` passed via forwarding reference.
-    static_assert(is_instantiation_of_v<async_closure_regular_arg, T>);
-    return vtag<safe_alias_of_v<typename T::storage_type>>;
-  }
-}
-
-// Returns a vtag of `safe_alias_v` for the storage type of the args that
-// did not come from `store_capture_binding`.
-//
-// We have to special-case the stored ones because `Captures.h` marks the
-// `capture` wrappers for on-closure stored values `unsafe` to discourage users
-// from moving them from the original closure.  And, the wrappers themselves
-// check the safety of the underlying type (via `capture_safety`).
-//
-// The doc in `scheduleScopeClosure()` justifies why our first call to this
-// function includes `ref_hack` args in the measurement (we want the
-// closure's own args downgraded to `after_cleanup_ref` safety), but not in
-// the second (we don't want the emitted `SafeTask` to be knocked down to
-// `shared_cleanup` safety, since that would make it unschedulable).
-template <bool ParentViewOfSafety, typename TransformedBindingList>
-constexpr auto vtag_safety_of_async_closure_args() {
-  return []<typename... T>(tag_t<T...>) {
-    return value_list_concat_t<
-        vtag_t,
-        decltype(vtag_safety_of_async_closure_arg<
-                 ParentViewOfSafety,
-                 T>())...>{};
-  }(TransformedBindingList{});
-}
-
-template <typename BindingT>
-constexpr bool capture_needs_outer_coro() {
-  using BP = folly::bindings::ext::binding_policy<BindingT>;
-  using ST = typename BP::storage_type;
-  return has_async_closure_co_cleanup<ST>;
-}
-
-struct async_closure_bindings_cfg {
-  bool force_outer_coro;
-  bool force_shared_cleanup;
-  bool is_invoke_member;
-};
-
-// For `is_invoke_member` closures, we must run an additional lifetime-safety
-// check.  For convenience, we also implicitly wrap the first argument with
-// `as_capture` when that's the obviously right choice.
-template <async_closure_bindings_cfg Cfg>
-struct async_closure_invoke_member_bindings {
-  constexpr auto operator()(tag_t<>) { return tag<>; }
-  template <auto BI0, typename BT0, auto... BI, typename... BT>
-  constexpr auto operator()(
-      tag_t<
-          folly::bindings::ext::binding_t<BI0, BT0>,
-          folly::bindings::ext::binding_t<BI, BT>...>) {
-    using T = std::remove_cvref_t<BT0>;
-    constexpr bool arg0_is_non_owning_ptr =
-        // `transform_binding()` passes captures as non-owning refs
-        is_any_capture<T> ||
-        // Raw pointers are allowed in `async_now_closure()`
-        std::is_pointer_v<T> ||
-        is_instantiation_of_v<folly::coro::AsyncObjectNonSlotPtr, T> ||
-        // `scheduleScopeClosure` & `scheduleSelfClosure` give non-owning
-        // pointers.  NB: This covers `SlotLimitedObjectPtr`.
-        is_instantiation_of_v<async_closure_scope_self_ref_hack, T>;
-    // Invoking a `MemberTask` requires `force_outer_coro` iff the first arg
-    // is an owning capture.
-    //
-    // NB: Both implicit & explicit `as_capture()`s are assumed to be owning,
-    // and thus also `force_outer_coro`.
-    //
-    // The reason that `force_outer_coro` is NOT done automatically is that
-    // it adds perf overhead, which would be easily avoided if the user made
-    // their member function `static` instead.
-    static_assert(
-        Cfg.force_outer_coro || !Cfg.is_invoke_member || arg0_is_non_owning_ptr,
-        "It looks like you want the `MemberTask` closure to own the object "
-        "instance. Use `async_now_closure(bound_args{&obj}, fn)` if that "
-        "applies. The next best approach is to make your task `static`, "
-        "with its first arg `auto self`. If that's not viable, then use "
-        "`async_closure_config{.force_outer_coro = true}` to allocate a "
-        "coro frame to own your object.");
-    // Syntax sugar: `as_capture()` may be left as implicit for the arg0
-    // "object parameter" of `FOLLY_INVOKE_MEMBER`.
-    if constexpr (
-        Cfg.is_invoke_member &&
-        // If arg0 is `as_capture()` or similar, don't double-wrap it.
-        !std::derived_from<decltype(BI0), capture_bind_info_t> &&
-        // Non-owning pointer-like things don't need to be captured.
-        !arg0_is_non_owning_ptr) {
-      static_assert(
-          // BT0 is a value for `make_in_place`, rval ref otherwise.
-          !std::is_lvalue_reference_v<BT0>,
-          "If you call `async_closure` with `FOLLY_INVOKE_MEMBER` and "
-          "a non-`capture` argument, then it has to be an r-value, so "
-          "that the closure can take ownership of the object instance. "
-          "Consider `folly::copy()` or `std::move()`.");
-      return tag<
-          folly::bindings::ext::
-              binding_t<as_capture_bind_info<capture_kind::plain>{}(BI0), BT0>,
-          folly::bindings::ext::binding_t<BI, BT>...>;
-    } else {
-      return tag<
-          folly::bindings::ext::binding_t<BI0, BT0>,
-          folly::bindings::ext::binding_t<BI, BT>...>;
-    }
-  }
-};
-
-// Converts forwarded arguments to bindings, figures out the storage policy
-// (outer coro?, shared cleanup?), and applies `transform_bindings` to compute
-// the final storage & binding outcome for each argument.  The caller should
-// create an outer coro iff the resulting `tuple` contains at least one
-// `async_closure_outer_stored_arg`.
-//
-// Returns a pair:
-//   - vtag<safe_alias> computed as in `vtag_safety_of_async_closure_arg()`
-//   - transformed bindings: binding | async_closure_{inner,outer}_stored_arg
-//
-// NB: It's fine for this implementation detail to take `BoundArgs` by-ref
-// because `async_closure` & friends took them by value.
-template <async_closure_bindings_cfg Cfg, typename BoundArgs>
-constexpr auto async_closure_safeties_and_bindings(BoundArgs&& bargs) {
-  using Bindings = decltype(async_closure_invoke_member_bindings<Cfg>{}(
-      typename BoundArgs::binding_list_t{}));
-
-  auto tup = static_cast<BoundArgs&&>(bargs).unsafe_tuple_to_bind();
-  auto make_result_tuple =
-      [&]<binding_helper_cfg HelperCfg>(vtag_t<HelperCfg>) {
-        return [&]<size_t... Is>(std::index_sequence<Is...>) {
-          return lite_tuple::tuple{[&]() {
-            using Binding = type_list_element_t<Is, Bindings>;
-            using T = std::tuple_element_t<Is, decltype(tup)>;
-            return capture_binding_helper<Binding, HelperCfg, Is>::
-                transform_binding(bind_wrapper_t<T>{
-                    .t_ = static_cast<T&&>(lite_tuple::get<Is>(tup))});
-          }()...};
-        }(std::make_index_sequence<type_list_size_v<Bindings>>{});
-      };
-
-  // Future: If there are many `make_in_place` arguments (which require
-  // `capture_heap`), it may be more efficient to auto-select an outer coro,
-  // for just 2 heap allocations.  Beware: this changes user-facing types
-  // (`capture_heap` to `capture`), but most users shouldn't depend on that.
-  constexpr bool has_outer_coro =
-      Cfg.force_outer_coro || []<typename... Bs>(tag_t<Bs...>) {
-        return (capture_needs_outer_coro<Bs>() || ...);
-      }(Bindings{});
-  // Figure out `IsSharedCleanupClosure` for `binding_cfg` for the real
-  // `transform_binding` call.
-  //
-  // Our choice of `is_shared_cleanup_closure = true` is important since we
-  // reuse this type list for the returned
-  // `vtag_safety_of_async_closure_args`.  That vtag is used by
-  // `async_closure` to compute the safety level for the resulting
-  // `SafeTask`.  This safety must NOT be increased by reference upgrades --
-  // a reference's safety is only upgraded inside the child closure, but the
-  // original safety applies in the parent closure, which is where the
-  // returned `vtag` is consumed.
-  //
-  // Choosing `true` here does not affect the `is_shared_cleanup` choice below
-  // It merely toggles between `after_cleanup_ref_capture` and `capture`, with
-  // either `after_cleanup_ref` or `co_cleanup_safe_ref` safety.
-  using shared_cleanup_transformed_binding_types = type_list_concat_t<
-      tag_t,
-      decltype(make_result_tuple(
-          vtag<binding_helper_cfg{
-              .is_shared_cleanup_closure = true,
-              .has_outer_coro = has_outer_coro,
-              .in_safety_measurement_pass = true}>))>;
-
-  // Why do we evaluate arg safety with `ParentViewOfSafety == true` here,
-  // and with `false` in the returned `vtag_safety_of_async_closure_args`?
-  //
-  // This toggle supports two usage scenarios:
-  //
-  // (1) Capture-by-reference behaviors, like `capture_const_ref()` /
-  // `as_capture(const_ref())` et al.
-  //    - `unsafe` for parent --  Since these are raw references from the
-  //      parent's scope, ensure they're only allowed in `async_now_closure`s.
-  //    - Ignored by child -- Simultaneously, we don't want the internal coro
-  //      to be subject to "shared cleanup" downgrades.  Doing that would,
-  //      e.g., break the useful pattern of an on-closure scope collecting
-  //      results on a parent collector passed via capture-by-reference.
-  //
-  // (2) Closures created by `spawn_self_closure()` et al.  Also see
-  // `async_closure_scope_self_ref_hack`.
-  //
-  //   - In the returned `vtag` that measures the parent's view of the safety
-  //     of the closure, `ParentViewOfSafety == true` will exclude the
-  //     closure's first arg (the scope or object ref) from the vtag -- it
-  //     would otherwise be `shared_cleanup`.  That is, of course, the entire
-  //     point of `spawn_self_closure()` -- we happen to know that the scope
-  //     ref is safe because of the circumstances of the closure's creation.
-  //
-  //   - Using `ParentViewOfSafety = false` here makes `spawn_self_closure`s
-  //     **internally** consider themselves to be `shared_cleanup` closures.
-  //     I.e. `after_cleanup_` inputs are not upgraded, and owned captures are
-  //     downgraded to `after_cleanup_`.
-  //
-  //     To see that these downgrades are the correct behavior, imagine a chain
-  //     of closures, each calling `spawn_self_closure()` to make the next.
-  //     `SafeAsyncScope` awaits these concurrently, so they must not take
-  //     dependencies on each other's owned captures.
-  constexpr auto internal_arg_min_safety = vtag_least_safe_alias(
-      vtag_safety_of_async_closure_args<
-          /*ParentViewOfSafety*/ false,
-          shared_cleanup_transformed_binding_types>());
-
-  // Compute the `after_cleanup_` downgrade/upgrade behavior for the closure.
-  // Two possible scenarios:
-  //  - An `async_closure` takes a `SafeTask` and emits a `SafeTask`.  Then
-  //    we'll have `==` iff we got a `co_cleanup_capture` ref from a parent.
-  //  - An `async_closure` taking an unconstrained task (may have by-ref
-  //    args, ref captures), and emitting a `NowTask`.  In this case, the arg
-  //    safety doesn't actually matter -- the caller must always
-  //    `force_shared_cleanup` simply because the lambda callable might
-  //    capture a `co_cleanup` ref inside it.
-  static_assert(
-      safe_alias::closure_min_arg_safety == safe_alias::shared_cleanup);
-  constexpr bool is_shared_cleanup = Cfg.force_shared_cleanup ||
-      (safe_alias::shared_cleanup >= internal_arg_min_safety);
-
-  return lite_tuple::tuple{
-      // Safety of the closure's arguments from the parent's perspective
-      vtag_safety_of_async_closure_args<
-          /*ParentViewOfSafety*/ true,
-          shared_cleanup_transformed_binding_types>(),
-      // How the child closure should store and/or bind its arguments
-      make_result_tuple(
-          vtag<binding_helper_cfg{
-              .is_shared_cleanup_closure = is_shared_cleanup,
-              .has_outer_coro = has_outer_coro,
-              .in_safety_measurement_pass = false}>)};
-}
-
-} // namespace folly::coro::detail
-
-FOLLY_POP_WARNING
-#endif
diff --git a/folly/folly/coro/safe/detail/BindAsyncClosure.h b/folly/folly/coro/safe/detail/BindAsyncClosure.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/coro/safe/detail/BindAsyncClosure.h
@@ -0,0 +1,878 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compare>
+
+#include <folly/coro/safe/BindCaptures.h>
+#include <folly/coro/safe/Captures.h>
+#include <folly/lang/SafeAlias.h>
+#include <folly/lang/bind/Named.h> // See test `AsyncClosure.captureBackref`
+#include <folly/lang/bind/NamedToStorage.h>
+
+/// This header's `async_closure_safeties_and_bindings` implements the
+/// argument-binding logic for `async_closure`.
+///
+/// Before reading further, make sure to get familiar with:
+///   - `folly/lang/Bindings.md` for `bind::args` & friends.
+///   - `docs/Captures.md` to understand the `capture` type wrappers, and the
+///     safety upgrade/downgrade rules for passing them into closures.
+/// In particular, know this distinction:
+///   * "owned captures" look like `capture<V>`.  These are wrappers tied
+///     to the closure whose `bind::capture()` created it. Note that a closure
+///     with an outer coro will pass these as `capture<V&>` to the inner task.
+///   * "capture references" are `capture<V&>` or `<V&&>, implicitly created
+///     for the closure from any caller-provided `capture` (value or ref).
+///
+/// `async_closure` takes user-specified closure arguments as `bind::args`, an
+/// immovable object with 1-expression lifetime.  This header plumbs them
+/// through some transformations.  In opt builds, it is intended to be elided
+/// by compiler's alias analysis (NB: this needs benchmarks & perhaps tweaks).
+///
+/// Every variant of `async_closure` invokes `..._safeties_and_bindings()`,
+/// which does several jobs.  Here's a summary of the data flow:
+///   * Figure out if the closure needs an outer coro, or if it can be elided.
+///   * Measure the safety of the closure, as seen by its caller.  This uses
+///     the safeties of the arguments **before** any transformations.
+///   * Transform the arg tuple:
+///       - Convert each entry of the `bind::args` into a `capture` ref (when
+///         the caller gave us a `capture`), or one of 4 tag types
+///         (`async_closure*_arg` or `async_closure*_self_ref_hack`).  The tag
+///         types tells the `async_closure` implementation whether to store the
+///         arg, and how to bind it to the inner closure.
+///       - Figure out the storage type for each `bind::capture` binding using
+///         `bind::bind_to_storage_policy`, to support `bind::in_place*`.
+///       - Transform non-owned `capture`s via `to_capture_ref`.  Parents'
+///         owned captures are implicitly passed by-ref; `after_cleanup_` refs
+///         are "upgraded" if possible.  Docs in `Captures.md`.
+///       - Apply special handling to the first argument when the closure runs
+///         `FOLLY_INVOKE_MEMBER`.
+///       - Other args are perfectly forwarded.
+///   * Validate the user inputs and try to issue readable error messages.
+///     Also check internal invariants.
+///
+/// After the validation & transformation, `detail/AsyncClosure.h` is
+/// responsible for actually storing the args, and creating the coroutine.
+///
+/// ## Implementation glossary
+///
+/// This header classifies the bound arguments into a few categories:
+///   * "owned capture" or just "own": Make a new `capture` whose storage (and
+///     cleanup) belongs to this closure.
+///       - These correspond to `async_closure_{inner,outer}_stored_arg`.
+///       - If the closure is shared-cleanup, the safety of the new capture
+///         is downgraded to `after_cleanup_ref`.
+///       - The inner task sees a `capture` ref for closures with an outer
+///         coro, and a `capture` value otherwise.
+///   * "pass capture ref" or just "pass": A `capture` from the caller.
+///       - These are passed as `capture<Ref>`, even if the input is a value.
+///       - The inner coro may see upgraded safety relative to the caller.
+///   * "regular arg": The easy / normal case -- simply bind a forwarding
+///     reference from the caller to the inner coro.  The reference is dressed
+///     in `async_closure_regular_arg`.
+///   * "self-reference hack": See `async_closure_scope_self_ref_hack`.
+
+#if FOLLY_HAS_IMMOVABLE_COROUTINES
+FOLLY_PUSH_WARNING
+FOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS
+
+// Future: If this is used anywhere else, move it to `BindCapturesToStorage.h`.
+//
+// We extended `bind::ext::bind_info_t` with `capture_kind`, so we must
+// explicitly specialize `bind_to_storage_policy`.  We reuse the standard
+// rules.  Custom `capture` binding logic lives in `BindAsyncClosure.h`.
+namespace folly::bind::ext {
+template <auto BI, typename BindingType>
+  requires std::same_as< // Written as a constraint to prevent object slicing
+      decltype(BI),
+      ::folly::bind::detail::capture_bind_info_t>
+class bind_to_storage_policy<binding_t<BI, BindingType>> {
+ private:
+  using standard =
+      bind_to_storage_policy<binding_t<bind_info_t{BI}, BindingType>>;
+
+ public:
+  using storage_type = typename standard::storage_type;
+  using signature_type = typename standard::signature_type;
+};
+} // namespace folly::bind::ext
+
+namespace folly::coro {
+class AsyncObject;
+class AsyncScopeSlotObject;
+template <typename, size_t>
+class safe_async_scope_context_proxy;
+template <typename>
+class AsyncObjectNonSlotPtr;
+} // namespace folly::coro
+
+namespace folly::coro::detail {
+
+template <safe_alias... Vs>
+constexpr safe_alias vtag_least_safe_alias(vtag_t<Vs...>) {
+  return std::min({safe_alias::maybe_value, Vs...});
+}
+
+//
+// There are 4 tag types here, which all quack the same interface:
+//   * `storage_type`: For storing "owned captures", but also for measuring
+//     "caller's point-of-view" safety of regular args.
+//   * `bindWrapper_`: A `bind_wrapper_t<T>`, which is literally just `T` but
+//     preserving the value category even for references.  It has either the
+//     forwarding reference or the value being bound.  Note that `T` is not
+//     necessarily related to `storage_type` -- for in-place construction, it
+//     is a "maker", which is implicitly-convertible to the `storage_type`.
+//
+// There's also 5th case without a tag type -- when passing capture refs,
+// `async_closure_safeties_and_bindings()` simply emits an unwrapped `capture`,
+// and `AsyncClosure.h` detects it via `is_any_capture<Bs>`.
+//
+// The goal is that none of the 5 cases instantiate any storage, or call
+// copy/move constructors until the final moment, when we either:
+//   - pass the arg to the inner coro, or
+//   - store it in the outer coro's `unique_ptr<tuple<>>` of owned captures.
+//
+
+// This is just a fancy forwarding reference, never a value.
+template <typename Storage, typename BindWrapper>
+struct async_closure_regular_arg {
+  using storage_type = Storage;
+  BindWrapper bindWrapper_;
+};
+
+// Use `is_base_of` since `instantiation_of` cannot handle no-type templates
+class async_closure_outer_stored_arg_base {};
+template <typename T>
+concept is_async_closure_outer_stored_arg =
+    std::is_base_of_v<async_closure_outer_stored_arg_base, T>;
+
+// For a given closure, all `_stored_arg` tags are going to be of one flavor.
+// With an outer coro, we capture info required to resolve backrefs.  Also, the
+// "has outer coro" decision isn't exported in any way besides the "inner" vs
+// "outer" stored arg type.  If useful, we could easily refactor this to be one
+// `_owned_capture` tag type, and branch on `has_outer_coro` in
+// `AsyncClosure.h`.  Reworking things this way would make it possible to have
+// an outer coro without storage, which might be needed if someone has a
+// legitimate use-case for captures that define `setParentCancelToken()` or
+// `setParentExecutor()` without defining `co_cleanup()` (dubious!).
+template <is_any_capture Storage, typename BindWrapper, size_t ArgI, auto Tag>
+struct async_closure_outer_stored_arg : async_closure_outer_stored_arg_base {
+  using storage_type = Storage;
+  constexpr static inline size_t arg_idx = ArgI;
+  constexpr static inline auto tag = Tag;
+  BindWrapper bindWrapper_;
+};
+template <is_any_capture Storage, typename BindWrapper>
+struct async_closure_inner_stored_arg {
+  using storage_type = Storage;
+  BindWrapper bindWrapper_;
+};
+
+// ## Why does this `self_ref_hack` type even exist?
+//
+// To avoid synchronization costs, `folly::coro` async scopes disallow `add()`
+// after `joinAsync()` has completed (violating this is UB).  However, it is
+// always safe to call `add()` on an async scope from a task running on that
+// **same** scope, even if `joinAsync()` has already started. This is because
+// any active scope awaitable prevents its `joinAsync()` from completing.
+//
+// Unfortunately, though it would be safe, you cannot directly pass an
+// `async_arg_cleanup<Scope&>` ref (`shared_cleanup` safety) into a closure
+// being scheduled on the SAME async scope (needs `>= co_cleanup_safe_ref`).
+// That's because at compile-time, we cannot tell if the "scope being scheduled
+// on" is the same as "the scope being referenced".
+//
+// Moreover, "pass a ref to the current scope" is the ONLY case that is always
+// safe [1].  Otherwise, the closure with the scope ref could run `add()` when
+// the (other) scope that it references had already been cleaned up.
+//
+// [1] Future: Thanks to `co_cleanup` ordering, it may later be feasible to
+// allow scopes that are passed later (cleaned up earlier) to reference those
+// that are passed earlier (cleaned up later).
+//
+// ## How does `self_ref_hack` help allow safe self-references for scopes?
+//
+// The solution is to introduce `scheduleScopeClosure()`, which acts just like
+// `schedule(async_closure())`, but prepends an "implicit scope parameter" to
+// the user-supplied `bind::args{}`.
+//
+// This implicit param is `self_ref_hack`, which is handled specially inside
+// `detail/AsyncClosure*`.  As a result, the user's inner task gets a
+// `co_cleanup_capture<Scope&>` as its first argument.
+//
+// Analogously, `AsyncObject::scheduleSelfClosure()` uses this mechanism to
+// allow sub-closures to safely reference the `Slot` of the object, which
+// contains the "current" async scope.
+//
+// ## Why are all aspects of `self_ref_hack` protected?
+//
+// This is NOT safe to use in other settings, because:
+// (1) We instantiates a new `capture` ref from a bare reference, bypassing the
+//     usual lifetime safety checks.
+// (2) This `ref_hack` object is deliberately excluded from the final
+//     `async_closure`'s safety accounting -- it only affects the
+//     `shard_cleanup` downgrade.
+// Both of these are ONLY okay because the sub-closure's own scope outlives it.
+template <typename Storage, typename BindWrapper>
+struct async_closure_scope_self_ref_hack {
+  using storage_type = Storage;
+
+ protected:
+  template <size_t, typename Bs, bool>
+  friend decltype(auto) async_closure_bind_inner_coro_arg(
+      capture_private_t, Bs&, auto&);
+  template <typename, size_t>
+  friend class folly::coro::safe_async_scope_context_proxy;
+  friend class folly::coro::AsyncScopeSlotObject;
+  // The innards are `protected`, since `transform_binding` lets this be
+  // supplied from outside the closure machinery.  Any new client being
+  // added here must think THOROUGHLY about the risks in the class docblock!
+  explicit async_closure_scope_self_ref_hack(BindWrapper b)
+      : bindWrapper_(std::move(b)) {}
+  BindWrapper bindWrapper_;
+};
+
+struct binding_helper_cfg {
+  bool is_shared_cleanup_closure;
+  bool has_outer_coro;
+  bool in_safety_measurement_pass;
+  constexpr auto operator<=>(const binding_helper_cfg&) const = default;
+};
+
+template <typename Binding, auto Cfg, size_t ArgI>
+class capture_binding_helper;
+
+struct capture_ref_measurement_stub {};
+
+// This helper class only has static members.  It exists only so that the
+// various functions can share some type aliases.
+template <
+    std::derived_from<bind::ext::bind_info_t> auto BI,
+    typename BindingType,
+    auto Cfg,
+    size_t ArgI>
+class capture_binding_helper<bind::ext::binding_t<BI, BindingType>, Cfg, ArgI> {
+ private:
+  // A constraint on the template would make forward-declarations messy.
+  static_assert(std::is_same_v<decltype(Cfg), binding_helper_cfg>);
+
+  using category_t = bind::ext::category_t;
+  using ST = typename bind::ext::bind_to_storage_policy<
+      bind::ext::binding_t<BI, BindingType>>::storage_type;
+  using UncvrefST = std::remove_cvref_t<ST>;
+
+  // "Pass capture ref" validation.  Here, `ST` is either a value or a
+  // reference `capture`.  `RetST` is the `capture<Ref>` for the inner task.
+  //
+  // There is no "business logic" here.  This only documents the possible data
+  // flows, `static_assert`s a few common user errors for better compiler
+  // messages, and refers to the relevant unit tests.
+  template <typename RetST>
+  static constexpr void static_assert_passing_capture() {
+    using ArgT = typename UncvrefST::capture_type;
+    static_assert(std::is_reference_v<ST> == (BI.category == category_t::ref));
+    if constexpr (std::is_reference_v<ArgT>) { // Is `capture<Ref>`?
+      // Design note: Why do we automatically pass all `capture`s by-reference?
+      //
+      // As an alternative, recall we have `bind::const_ref` / `bind::mut_ref`.
+      // These are now used for `bind::capture_const_ref` and
+      // `bind::capture_mut_ref` with different semantics (below).  Why didn't
+      // we instead use these as mandatory markings for `captures` that get
+      // passed by-reference.  That might seem more explicit, but also more
+      // confusing and harder to use:
+      //   - `const_ref` / `mut_ref CANNOT be used for "regular" args -- they're
+      //     by-reference iff the caller writes `T&` in the signature.
+      //   - `capture`s are intended to belong to the parent closure, it rarely
+      //     makes sense to copy or move them.
+      //   - Syntactically, `capture`s behave like pointers.
+      //   - If we needed an explicit `const_ref` / `mut_ref` only to pass
+      //     `capture<Val>` as a `capture<Ref>`, then migrating a closure from
+      //     "has outer coro" to "lacks outer coro" would require adding such a
+      //     modifier at every callsite.
+      //   - You can still move out the contents of a capture into a child by
+      //     passing an rvalue ref as `std::move(cap)` to the child, or by
+      //     passing the actual value via `*std::move(cap)`.  Similarly, `*cap`
+      //     would copy the value.
+      //
+      // N.B.  We DO use `bind::capture{const_ref{}}` etc in order to convert
+      // plain references from a parent coro into `capture` refs in a child
+      // closure, see "capture-by-reference" in `Captures.md`.
+      static_assert(
+          !std::is_reference_v<ST>,
+          "Pass `capture<Ref>` by value, do not use `const_ref` / `mut_ref`");
+      // Passing the caller's `capture<Ref>` makes a new `capture` ref object.
+      if constexpr (std::is_lvalue_reference_v<BindingType>) {
+        // Improve errors over just "deleted copy ctor".
+        static_assert(
+            !std::is_rvalue_reference_v<ArgT>,
+            "capture<V&&> is move-only. Try std::move(yourRef).");
+
+        // `check_capture_lref_to_lref` tests this branch -- passing a
+        // `capture<Ref>` that the caller bound as an lvalue.  We'll copy it,
+        // potentially upgrading `after_cleanup_capture` -> `capture.
+      } else {
+        // Cleanup args don't support rval refs.  No user-facing message, since
+        // `co_cleanup_capture` would first have a constraint failure.
+        static_assert(!is_any_co_cleanup_capture<UncvrefST>);
+
+        // Tests: `check_capture_lref_to_rref` & `check_capture_rref_to_rref`.
+        // An input `capture<Ref>` bound as an rvalue gives the child a
+        // `capture<Val&&>`.
+        //
+        // It is in some sense optional to support this, since users can
+        // pass around lval refs and `std::move(*argRef)` at the last
+        // minute.  However, I wanted to encourage the best practice of
+        // `std::move(argRef)` at the outermost callsite that knows about
+        // the move.  Reasons:
+        //   - The initial `std::move(arg)` enables use-after-move linting
+        //     in the outermost scope.
+        //   - `capture<T&&>` is move-only, meaning subsequent scopes also
+        //     get use-after-move linting.
+        //   - Future: we could build a debug-only use-after-move runtime
+        //     checker by adding some state on `capture`s.
+      }
+    } else { // Is `capture<Val>`?
+      // Cleanup args require an outer coro, so it should never be the case
+      // that `*co_cleanup_capture<Val>` is being passed to a child..
+      static_assert(!is_any_co_cleanup_capture<UncvrefST>);
+
+      // Tested in `check_capture_val_to_ref`: `capture<Val>` is implicitly
+      // passed as `capture<Ref>`.
+    }
+  }
+
+  template <typename T>
+  static constexpr auto store_as(auto bind_wrapper) {
+    if constexpr (Cfg.has_outer_coro) {
+      return async_closure_outer_stored_arg<
+          T,
+          decltype(bind_wrapper),
+          ArgI,
+          bind::ext::named_bind_info_tag_v<decltype(BI)>>{
+          .bindWrapper_ = std::move(bind_wrapper)};
+    } else {
+      return async_closure_inner_stored_arg<T, decltype(bind_wrapper)>{
+          .bindWrapper_ = std::move(bind_wrapper)};
+    }
+  }
+
+  // "owned capture": The closure creates storage for `bind::capture()` bindings
+  static constexpr auto store_capture_binding(auto bind_wrapper) {
+    static_assert(
+        !is_any_capture<ST>,
+        "Given a capture `c`, do not write `bind::capture(c)` to pass it to a "
+        "closure. Just write `c` as the argument, and it'll automatically "
+        "be passed as a capture reference.");
+    if constexpr (has_async_closure_co_cleanup<ST>) {
+      static_assert(Cfg.has_outer_coro);
+      // Future: Add a toggle to emit `restricted_co_cleanup_capture`
+      return store_as<co_cleanup_capture<ST>>(std::move(bind_wrapper));
+    } else if constexpr (
+        BI.captureKind_ == bind::detail::capture_kind::indirect) {
+      if constexpr (Cfg.is_shared_cleanup_closure) {
+        return store_as<after_cleanup_capture_indirect<ST>>(
+            std::move(bind_wrapper));
+      } else {
+        return store_as<capture_indirect<ST>>(std::move(bind_wrapper));
+      }
+    } else if constexpr (
+        !Cfg.has_outer_coro &&
+        // `bind::in_place*` is often used for immovable types, so without an
+        // outer coro, they must be on-heap to pass ownership to the inner coro.
+        bind::ext::is_binding_t_type_in_place<BindingType> &&
+        // Heuristic: Moving a type is usually cheaper than putting it on
+        // the heap.  If not, people can always use `capture_indirect` with
+        // `unique_ptr`...  Or, we could later add new capture kinds, like
+        // `plain_auto_storage = 0`, `plain_heap`, and `plain_non_heap`.
+        !std::is_move_constructible_v<BindingType>) {
+      if constexpr (Cfg.is_shared_cleanup_closure) {
+        return store_as<after_cleanup_capture_heap<ST>>(
+            std::move(bind_wrapper));
+      } else {
+        return store_as<capture_heap<ST>>(std::move(bind_wrapper));
+      }
+    } else {
+      if constexpr (Cfg.is_shared_cleanup_closure) {
+        return store_as<after_cleanup_capture<ST>>(std::move(bind_wrapper));
+      } else {
+        return store_as<capture<ST>>(std::move(bind_wrapper));
+      }
+    }
+  }
+
+  template <typename>
+  static inline constexpr bool is_supported_capture_bind_info_v = false;
+
+  template <>
+  inline constexpr bool
+      is_supported_capture_bind_info_v<bind::detail::capture_bind_info_t> =
+          true;
+
+  // Future: Right now, we only check that `"x"_id = ` tags are unique at time
+  // of use, and this only applies for stored captures.  But, from a pure "code
+  // quality" point of view, it would be reasonable to demand that all tags are
+  // unique, and that they are all used.  This could be done either as a linter
+  // or in this file, at some compile-time cost.
+  template <auto Tag>
+  static inline constexpr bool is_supported_capture_bind_info_v<
+      bind::ext::named_bind_info_t<Tag, bind::detail::capture_bind_info_t>> =
+      true;
+
+ public:
+  // Transforms the binding as per the file docblock, returns a new binding.
+  // (either one of the 4 tag types above, or `capture<Ref>`)
+  static constexpr auto transform_binding(auto bind_wrapper) {
+    if constexpr (is_supported_capture_bind_info_v<decltype(BI)>) {
+      // Implement "capture-by-reference", docs in `Captures.md`
+      if constexpr (BI.category == category_t::ref) {
+        // Test in `check_parent_capture_ref`
+        static_assert(std::is_reference_v<ST>);
+        static_assert(
+            !is_any_capture<UncvrefST>,
+            "Do not use `const_ref` / `mut_ref` verbs to pass a `capture` to "
+            "a child closure -- just pass it directly.");
+        // It should be hard to get a ref to a co_cleanup type
+        static_assert(!has_async_closure_co_cleanup<UncvrefST>);
+        if constexpr (Cfg.in_safety_measurement_pass) {
+          return capture_ref_measurement_stub{};
+        } else if constexpr (Cfg.is_shared_cleanup_closure) {
+          return after_cleanup_capture<ST>{
+              capture_private_t{}, std::move(bind_wrapper)};
+        } else {
+          return capture<ST>{capture_private_t{}, std::move(bind_wrapper)};
+        }
+      } else { // Tests in `check_stored_*`.
+        static_assert(!std::is_reference_v<ST>);
+        return store_capture_binding(std::move(bind_wrapper));
+      }
+    } else { // Bindings for arguments the closure does NOT store.
+      static_assert(
+          std::is_same_v<vtag_t<BI>, vtag_t<bind::ext::bind_info_t{}>>,
+          "`folly::bind::` modifiers like `constant` (or `\"x\"_id = `) "
+          "only make sense with `bind::capture()` bindings -- for example, to "
+          "move a mutable value into `const` capture storage. For regular "
+          "args, use `const` in the signature of your inner coro, and/or "
+          "`std::as_const` when passing the arg.");
+      // If we allowed `bind::in_place` without `bind::capture`, the argument
+      // would require a copy or a move to be passed to the inner task (which
+      // the type may not support).  If `bind::capture` isn't appropriate, the
+      // user can also work around that via `std::make_unique<TheirType>` and/or
+      // `bind::capture_indirect`.
+      static_assert(
+          !bind::ext::is_binding_t_type_in_place<BindingType>,
+          "Did you mean `bind::capture_in_place<T>(...)`?");
+      if constexpr (is_any_capture<UncvrefST>) { // Tests in `check_capture_*`
+        // Pass preexisting `capture`s (NOT owned by this closure).
+        // Future: Add a toggle to make `restricted_co_cleanup_capture` refs.
+        auto arg_ref =
+            std::move(bind_wrapper)
+                .what_to_bind()
+                .template to_capture_ref<Cfg.is_shared_cleanup_closure>(
+                    capture_private_t{});
+        static_assert_passing_capture<decltype(arg_ref)>();
+        return std::move(arg_ref);
+      } else if constexpr (
+          is_instantiation_of_v<async_closure_scope_self_ref_hack, UncvrefST>) {
+        // This `ref_hack` type quacks like the `stored_arg` types, but we need
+        // to unwrap it for it to be handled correctly downstream.
+        return std::move(bind_wrapper).what_to_bind();
+      } else { // Test in `check_regular_args`
+        // "regular" args -- neither an owned capture (`bind::capture()` et al),
+        // nor a parent's `capture`. Passed via forwarding reference.
+
+        // This may be redundant, since `co_cleanup_capture` enforces that
+        // cleanup types are immovable.  If we did allow passing bare
+        // `co_cleanup` types, it could violate memory safety protections for
+        // `async_closure`s.  For `async_now_closure`, there is also no obvious
+        // use-case for passing `*captureVar` by-reference into the child.
+        static_assert(
+            !has_async_closure_co_cleanup<UncvrefST>,
+            "This argument implements `async_closure` cleanup, so you should "
+            "almost certainly pass it `bind::capture()` -- or, if you already "
+            "have as a reference `capture`, by-value.");
+
+        return async_closure_regular_arg<ST, decltype(bind_wrapper)>{
+            .bindWrapper_ = std::move(bind_wrapper)};
+      }
+    }
+  }
+};
+
+// See `vtag_safety_of_async_closure_args` for the docs.
+// NB: As a nested lambda, this breaks on clang-17 due to compiler bugs.
+template <bool ParentViewOfSafety, typename T>
+auto vtag_safety_of_async_closure_arg() {
+  // "owned capture": `store_as` outputs `async_closure_*_stored_arg`.
+  if constexpr (
+      is_async_closure_outer_stored_arg<T> ||
+      is_instantiation_of_v<async_closure_inner_stored_arg, T>) {
+    using CT = typename T::storage_type::capture_type;
+    static_assert(!std::is_reference_v<CT>);
+    // Stored captures are as safe as the type being stored.  For example, when
+    // a closure stores a `BackgroundTask<Safety, T>`, it cannot be safer than
+    // `Safety`.
+    //
+    // While this replicates `lenient_safe_alias_of_v` logic, we don't directly
+    // use it here, since `AsyncObject.h` specializes `capture_safety_impl_v`.
+    return vtag<capture_safety_impl_v<CT, safe_alias::maybe_value>>;
+  } else if constexpr ( //
+      is_instantiation_of_v<async_closure_scope_self_ref_hack, T>) {
+    // This is a closure made by `spawn_self_closure()` et al. It must:
+    //  - Avoid marking the closure's outer task `shared_cleanup`, so it can
+    //    still be added to the scope that made it (`if` branch).
+    //  - Downgrade [*] the safety of its own captures (`else` branch).
+    //
+    // [*] It would be memory-unsafe to reference such captures from
+    // recursively scheduled closures on the same scope!
+    if constexpr (ParentViewOfSafety) {
+      return vtag<>;
+    } else {
+      constexpr auto storage_safety =
+          lenient_safe_alias_of_v<typename T::storage_type>;
+      // In current usage, ref_hack can only contain `co_cleanup_capture<V&>`.
+      static_assert(storage_safety == safe_alias::shared_cleanup);
+      return vtag<storage_safety>;
+    }
+  } else if constexpr (is_any_capture<T>) {
+    // "pass capture ref": Output of the `to_capture_ref` branch.
+    static_assert(std::is_reference_v<typename T::capture_type>);
+    return vtag<lenient_safe_alias_of_v<T>>;
+  } else if constexpr (std::is_same_v<capture_ref_measurement_stub, T>) {
+    if constexpr (ParentViewOfSafety) {
+      // Only allow capture-by-reference in `async_now_closure`s
+      return vtag<safe_alias::unsafe>;
+    } else {
+      // But, don't do closure-internal downgrades, since `transform_bindings`
+      // tries to prevent it from taking in refs to co_cleanup types this way.
+      return vtag<>;
+    }
+  } else {
+    // "regular arg": A non-`capture` passed via forwarding reference.
+    static_assert(is_instantiation_of_v<async_closure_regular_arg, T>);
+    return vtag<lenient_safe_alias_of_v<typename T::storage_type>>;
+  }
+}
+
+// Returns a vtag of `safe_alias_v` for the storage type of the args that
+// did not come from `store_capture_binding`.
+//
+// We have to special-case the stored ones because `Captures.h` marks the
+// `capture` wrappers for on-closure stored values `unsafe` to discourage users
+// from moving them from the original closure.  And, the wrappers themselves
+// check the safety of the underlying type (via `capture_safety`).
+//
+// The doc in `scheduleScopeClosure()` justifies why our first call to this
+// function includes `ref_hack` args in the measurement (we want the
+// closure's own args downgraded to `after_cleanup_ref` safety), but not in
+// the second (we don't want the emitted `safe_task` to be knocked down to
+// `shared_cleanup` safety, since that would make it unschedulable).
+template <bool ParentViewOfSafety, typename TransformedBindingList>
+constexpr auto vtag_safety_of_async_closure_args() {
+  return []<typename... T>(tag_t<T...>) {
+    return value_list_concat_t<
+        vtag_t,
+        decltype(vtag_safety_of_async_closure_arg<
+                 ParentViewOfSafety,
+                 T>())...>{};
+  }(TransformedBindingList{});
+}
+
+template <typename BindingT>
+constexpr bool capture_needs_outer_coro() {
+  using BP = bind::ext::bind_to_storage_policy<BindingT>;
+  using ST = typename BP::storage_type;
+  return has_async_closure_co_cleanup<ST>;
+}
+
+struct async_closure_bindings_cfg {
+  bool force_outer_coro;
+  bool emit_now_task;
+  bool is_invoke_member;
+};
+
+// For `is_invoke_member` closures, we must run an additional lifetime-safety
+// check.  For convenience, we also implicitly wrap the first argument with
+// `bind::capture` when that's the obviously right choice.
+template <async_closure_bindings_cfg Cfg>
+struct async_closure_invoke_member_bindings {
+  constexpr auto operator()(tag_t<>) { return tag<>; }
+  template <auto BI0, typename BT0, auto... BI, typename... BT>
+  constexpr auto operator()(
+      tag_t<bind::ext::binding_t<BI0, BT0>, bind::ext::binding_t<BI, BT>...>) {
+    using T = std::remove_cvref_t<BT0>;
+    constexpr bool arg0_is_non_owning_ptr =
+        // `transform_binding()` passes captures as non-owning refs
+        is_any_capture<T> ||
+        // Raw pointers are allowed in `async_now_closure()`
+        std::is_pointer_v<T> ||
+        is_instantiation_of_v<folly::coro::AsyncObjectNonSlotPtr, T> ||
+        // `scheduleScopeClosure` & `scheduleSelfClosure` give non-owning
+        // pointers.  NB: This covers `SlotLimitedObjectPtr`.
+        is_instantiation_of_v<async_closure_scope_self_ref_hack, T>;
+    // Invoking a `member_task` requires `force_outer_coro` iff the first arg
+    // is an owning capture.
+    //
+    // NB: Both implicit & explicit `bind::capture()`s are assumed to be owning,
+    // and thus also `force_outer_coro`.
+    //
+    // The reason that `force_outer_coro` is NOT done automatically is that
+    // it adds perf overhead, which would be easily avoided if the user made
+    // their member function `static` instead.
+    static_assert(
+        Cfg.force_outer_coro || !Cfg.is_invoke_member || arg0_is_non_owning_ptr,
+        "It looks like you want the `member_task` closure to own the object "
+        "instance. Use `async_now_closure(bind::args{&obj}, fn)` if that "
+        "applies. The next best approach is to make your task `static`, "
+        "with its first arg `auto self`. If that's not viable, then use "
+        "`async_closure_config{.force_outer_coro = true}` to allocate a "
+        "coro frame to own your object.");
+    // Syntax sugar: `bind::capture()` may be left as implicit for the arg0
+    // "object parameter" of `FOLLY_INVOKE_MEMBER`.
+    if constexpr (
+        Cfg.is_invoke_member &&
+        // If arg0 is `bind::capture()` or similar, don't double-wrap it.
+        !std::derived_from<decltype(BI0), bind::detail::capture_bind_info_t> &&
+        // Non-owning pointer-like things don't need to be captured.
+        !arg0_is_non_owning_ptr) {
+      static_assert(
+          // BT0 is a value for `bind::in_place`, rval ref otherwise.
+          !std::is_lvalue_reference_v<BT0>,
+          "If you call `async_closure` with `FOLLY_INVOKE_MEMBER` and "
+          "a non-`capture` argument, then it has to be an r-value, so "
+          "that the closure can take ownership of the object instance. "
+          "Consider `folly::copy()` or `std::move()`.");
+      return tag<
+          bind::ext::binding_t<
+              bind::detail::capture_bind_info<
+                  bind::detail::capture_kind::plain>{}(BI0),
+              BT0>,
+          bind::ext::binding_t<BI, BT>...>;
+    } else {
+      return tag<
+          bind::ext::binding_t<BI0, BT0>,
+          bind::ext::binding_t<BI, BT>...>;
+    }
+  }
+};
+
+template <safe_alias ParentView, bool ArgsForceSharedCleanup>
+struct async_closure_arg_safety {
+  static inline constexpr auto parent_view = ParentView;
+  static inline constexpr auto args_force_shared_cleanup =
+      ArgsForceSharedCleanup;
+};
+
+// Converts forwarded arguments to bindings, figures out the storage policy
+// (outer coro?, shared cleanup?), and applies `transform_bindings` to compute
+// the final storage & binding outcome for each argument.  The caller should
+// create an outer coro iff the resulting `tuple` contains at least one
+// `async_closure_outer_stored_arg`.
+//
+// Returns a 3-tuple:
+// [0] `async_closure_arg_safety`, see `vtag_safety_of_async_closure_arg()`
+// [1] A tuple `transform_bindings()` outputs, containing:
+//       binding OR async_closure_{inner,outer}_stored_arg, ...
+//     This tuple is always used by `bind_captures_to_closure`, at least for
+//     type computations, if not for creating the actual inner coro.  It is
+//     computed as if the closure's shared-cleanup behavior were fully
+//     determined by the bound args we see here.
+// [2] Either `nullptr`, OR a tuple of `transform_bindings()` outputs identical
+//     to [1], except that shared-cleanup is force-enabled.  This tuple is only
+//     computed for the cases when `bind_captures_to_closure` would use it.
+//
+// NB: It's fine for this implementation detail to take `BoundArgs` by-ref
+// because `async_closure` & friends took them by value.
+template <async_closure_bindings_cfg Cfg, typename BoundArgs>
+constexpr auto async_closure_safeties_and_bindings(BoundArgs&& bargs) {
+  using Bindings = decltype(async_closure_invoke_member_bindings<Cfg>{}(
+      typename BoundArgs::binding_list_t{}));
+
+  auto tup = static_cast<BoundArgs&&>(bargs).unsafe_tuple_to_bind();
+  auto make_bindings = [&]<binding_helper_cfg HelperCfg>(vtag_t<HelperCfg>) {
+    return [&]<size_t... Is>(std::index_sequence<Is...>) {
+      return lite_tuple::tuple{[&]() {
+        using Binding = type_list_element_t<Is, Bindings>;
+        using T = std::tuple_element_t<Is, decltype(tup)>;
+        return capture_binding_helper<Binding, HelperCfg, Is>::
+            transform_binding(
+                bind_wrapper_t<T>{
+                    .t_ = static_cast<T&&>(lite_tuple::get<Is>(tup))});
+      }()...};
+    }(std::make_index_sequence<type_list_size_v<Bindings>>{});
+  };
+
+  // Future: If there are many `bind::in_place` arguments (which require
+  // `capture_heap`), it may be more efficient to auto-select an outer coro,
+  // for just 2 heap allocations.  Beware: this changes user-facing types
+  // (`capture_heap` to `capture`), but most users shouldn't depend on that.
+  constexpr bool has_outer_coro =
+      Cfg.force_outer_coro || []<typename... Bs>(tag_t<Bs...>) {
+        return (capture_needs_outer_coro<Bs>() || ...);
+      }(Bindings{});
+  // Figure out `IsSharedCleanupClosure` for `binding_cfg` for the real
+  // `transform_binding` call.
+  //
+  // Our choice of `is_shared_cleanup_closure = true` is important since we
+  // reuse this type list for the returned
+  // `vtag_safety_of_async_closure_args`.  That vtag is used by
+  // `async_closure` to compute the safety level for the resulting
+  // `safe_task`.  This safety must NOT be increased by reference upgrades --
+  // a reference's safety is only upgraded inside the child closure, but the
+  // original safety applies in the parent closure, which is where the
+  // returned `vtag` is consumed.
+  //
+  // Choosing `true` here does not affect the `is_shared_cleanup` choice below
+  // It merely toggles between `after_cleanup_ref_capture` and `capture`, with
+  // either `after_cleanup_ref` or `co_cleanup_safe_ref` safety.
+  using shared_cleanup_transformed_binding_types = type_list_concat_t<
+      tag_t,
+      decltype(make_bindings(
+          vtag<binding_helper_cfg{
+              .is_shared_cleanup_closure = true,
+              .has_outer_coro = has_outer_coro,
+              .in_safety_measurement_pass = true}>))>;
+  // The template args store two views of the safety of the closure's args:
+  //   - As seen by the parent, where `ParentViewOfSafety == true`,
+  //   - As used for internal safety computations.
+  //
+  // This toggle supports two usage scenarios:
+  //
+  // (1) Capture-by-reference behaviors, like `bind::capture_const_ref()` /
+  // `bind::capture(const_ref())` et al.
+  //    - `unsafe` for parent --  Since these are raw references from the
+  //      parent's scope, ensure they're only allowed in `async_now_closure`s.
+  //    - Ignored by child -- Simultaneously, we don't want the internal coro
+  //      to be subject to shared-cleanup downgrades.  Doing that would,
+  //      e.g., break the useful pattern of an on-closure scope collecting
+  //      results on a parent collector passed via capture-by-reference.
+  //
+  // (2) Closures created by `spawn_self_closure()` et al.  Also see
+  // `async_closure_scope_self_ref_hack`.
+  //
+  //   - In the returned `vtag` that measures the parent's view of the safety
+  //     of the closure, `ParentViewOfSafety == true` will exclude the
+  //     closure's first arg (the scope or object ref) from the vtag -- it
+  //     would otherwise be `shared_cleanup`.  That is, of course, the entire
+  //     point of `spawn_self_closure()` -- we happen to know that the scope
+  //     ref is safe because of the circumstances of the closure's creation.
+  //
+  //   - Using `ParentViewOfSafety = false` here makes `spawn_self_closure`s
+  //     **internally** consider themselves to be `shared_cleanup` closures.
+  //     I.e. `after_cleanup_` inputs are not upgraded, and owned captures are
+  //     downgraded to `after_cleanup_`.
+  //
+  //     To see that these downgrades are the correct behavior, imagine a chain
+  //     of closures, each calling `spawn_self_closure()` to make the next.
+  //     `safe_async_scope` awaits these concurrently, so they must not take
+  //     dependencies on each other's owned captures.
+  async_closure_arg_safety<
+      // parent_view=
+      vtag_least_safe_alias(
+          vtag_safety_of_async_closure_args<
+              /*ParentViewOfSafety*/ true,
+              shared_cleanup_transformed_binding_types>()),
+      // args_force_shared_cleanup=
+      (safe_alias::shared_cleanup >=
+       vtag_least_safe_alias(
+           vtag_safety_of_async_closure_args<
+               /*ParentViewOfSafety*/ false,
+               shared_cleanup_transformed_binding_types>()))>
+      arg_min_safety;
+
+  // How this async-closure will store and/or bind its arguments depends on
+  // whether we have to force shared-cleanup downgrades for its args.
+  //
+  // This can happen in two ways:
+  //
+  // (1) `async_closure` takes a `safe_task` and emits a `safe_task`.  Just
+  //     above, we computed` `args_force_shared_cleanup`, which says that the
+  //     closure needs shared-cleanup iff a parent passes a
+  //     `co_cleanup_capture` ref.
+  static_assert(
+      safe_alias::closure_min_arg_safety == safe_alias::shared_cleanup);
+
+  // (2) `async_now_closure` always emits a `now_task`, which is meant
+  //     to be usable even with unsafe coros & arguments.
+  //
+  //     However, we only need shared-cleanup if the inner-coro COULD access a
+  //     ref of safety <= `shared_cleanup`.  Above, we tested the explicit
+  //     inputs via `args_force_shared_cleanup`, but that misses the "implicit
+  //     object parameter" (aka `this`) of the coro callable.
+  //
+  //     For example, `async_now_closure(..., [&]() -> now_task<> { ...  })`
+  //     compiles, and can access potentially-unsafe refs via the lambda's
+  //     `this` pointer, which gets access to raw refs into the outer scope.
+  //
+  //     Since `make_inner_coro` is evaluated synchronously, it is allowed to
+  //     be a "coro wrapper", i.e. a plain lambda with unsafe captures that
+  //     calls an actual coroutine function.  We only care about the safety of
+  //     the resulting coro.  Luckily, `is_safe_task_valid` from `SafeTask.h`
+  //     constrains the safety of the implicit object parameter.  Specifically,
+  //     we know that a `safe_task` with `ArgSafety >= unsafe_internal_closure`
+  //     requires[*] the implicit object parameter to have safety `>
+  //     shared_cleanup` (yes, greater than!), which is exactly our goal here.
+  //
+  //     To sum up: Above, we saw that if `async_now_closure`'s inner coro has
+  //     safety `>= unsafe_closure_internal`, then it should not have access to
+  //     any refs of safety `<= shared_cleanup`.
+  //
+  //     HOWEVER, here, in `async_closure_safeties_and_bindings`, we do not yet
+  //     know the inner coro result type, and cannot test its safety.  In fact,
+  //     we need to synthesize the argument types for `make_inner_coro` just to
+  //     resolve the right overload.  As a workaround, the returned 3-tuple
+  //     provides bindings for BOTH options -- by tuple index:
+  //
+  //     [1] `task_forces_shared_cleanup == false`.  Use these arg bindings,
+  //         made with `.is_shared_cleanup == args_force_shared_cleanup`, to
+  //         compute the inner coro type.  Use them to actually make the inner
+  //         coro ONLY IF it has safety `>= unsafe_closure_internal`.
+  //
+  //     [2] `task_forces_shared_cleanup == true`.  These arg bindings are
+  //         computed optionally (condition below), by setting
+  //         `.is_shared_cleanup == true`.  They will be used to make a
+  //         fallback inner coro if the plan from [1] didn't work.  We force
+  //         shared cleanup because, per above, the unsafe inner coro may have
+  //         access to unsafe refs.
+  auto maybe_make_bindings_for =
+      [&]<bool task_forces_shared_cleanup>(vtag_t<task_forces_shared_cleanup>) {
+        if constexpr (
+            // Always run `make_bindings` for index 1 of the returned `tuple`.
+            !task_forces_shared_cleanup ||
+            // Only run `make_bindings` for index 2 IF it may get used.
+            (Cfg.emit_now_task && !arg_min_safety.args_force_shared_cleanup)) {
+          return make_bindings(
+              vtag<binding_helper_cfg{
+                  .is_shared_cleanup_closure = task_forces_shared_cleanup ||
+                      arg_min_safety.args_force_shared_cleanup,
+                  .has_outer_coro = has_outer_coro,
+                  .in_safety_measurement_pass = false}>);
+        } else {
+          return nullptr; // result[2] will not be used
+        }
+      };
+
+  return lite_tuple::tuple{
+      arg_min_safety,
+      // The doc above `maybe_make_bindings_for` explains how these are used.
+      maybe_make_bindings_for(vtag</*task_forces_shared_cleanup*/ false>),
+      maybe_make_bindings_for(vtag</*task_forces_shared_cleanup*/ true>)};
+}
+
+} // namespace folly::coro::detail
+
+FOLLY_POP_WARNING
+#endif
diff --git a/folly/folly/debugging/exception_tracer/ExceptionCounterLib.cpp b/folly/folly/debugging/exception_tracer/ExceptionCounterLib.cpp
--- a/folly/folly/debugging/exception_tracer/ExceptionCounterLib.cpp
+++ b/folly/folly/debugging/exception_tracer/ExceptionCounterLib.cpp
@@ -26,7 +26,6 @@
 #include <folly/synchronization/RWSpinLock.h>
 
 #include <folly/debugging/exception_tracer/Compatibility.h>
-#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>
 #include <folly/debugging/exception_tracer/StackTrace.h>
 #include <folly/experimental/symbolizer/Symbolizer.h>
 
@@ -101,16 +100,12 @@
   return out;
 }
 
-} // namespace exception_tracer
-} // namespace folly
-
-namespace {
-
 /*
  * This handler gathers statistics on all exceptions thrown by the program
  * Information is being stored in thread local storage.
  */
-void throwHandler(void*, std::type_info* exType, void (**)(void*)) noexcept {
+void exceptionStatsThrowHandler(
+    void*, std::type_info* exType, void (**)(void*)) noexcept {
   // This array contains the exception type and the stack frame
   // pointers so they get all hashed together.
   uintptr_t frames[kMaxFrames + 1];
@@ -139,13 +134,8 @@
   });
 }
 
-struct Initializer {
-  Initializer() { registerCxaThrowCallback(throwHandler); }
-};
-
-Initializer initializer;
-
-} // namespace
+} // namespace exception_tracer
+} // namespace folly
 
 #endif //  FOLLY_HAS_EXCEPTION_TRACER
 
diff --git a/folly/folly/debugging/exception_tracer/ExceptionCounterLib.h b/folly/folly/debugging/exception_tracer/ExceptionCounterLib.h
--- a/folly/folly/debugging/exception_tracer/ExceptionCounterLib.h
+++ b/folly/folly/debugging/exception_tracer/ExceptionCounterLib.h
@@ -35,6 +35,13 @@
 };
 
 /**
+ * Throw handler that intercepts exception throwing and accumulates stats.
+ * Use with registerCxaThrowCallback/unregisterCxaThrowCallback.
+ */
+void exceptionStatsThrowHandler(
+    void*, std::type_info* exType, void (**)(void*)) noexcept;
+
+/**
  * This function accumulates exception throwing statistics across all threads.
  * Please note, that during call to this function, other threads might block
  * on exception throws, so it should be called seldomly.
diff --git a/folly/folly/debugging/exception_tracer/ExceptionCounterLibStaticRegistration.cpp b/folly/folly/debugging/exception_tracer/ExceptionCounterLibStaticRegistration.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/debugging/exception_tracer/ExceptionCounterLibStaticRegistration.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/debugging/exception_tracer/ExceptionCounterLib.h>
+#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>
+
+#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF && FOLLY_HAS_EXCEPTION_TRACER
+
+namespace {
+
+struct Initializer {
+  Initializer() {
+    folly::exception_tracer::registerCxaThrowCallback(
+        folly::exception_tracer::exceptionStatsThrowHandler);
+  }
+};
+
+Initializer initializer;
+
+} // namespace
+
+#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF && FOLLY_HAS_EXCEPTION_TRACER
diff --git a/folly/folly/debugging/exception_tracer/ExceptionStackTraceLib.cpp b/folly/folly/debugging/exception_tracer/ExceptionStackTraceLib.cpp
--- a/folly/folly/debugging/exception_tracer/ExceptionStackTraceLib.cpp
+++ b/folly/folly/debugging/exception_tracer/ExceptionStackTraceLib.cpp
@@ -22,7 +22,6 @@
 #include <folly/debugging/exception_tracer/ExceptionTracer.h>
 #include <folly/debugging/exception_tracer/ExceptionTracerLib.h>
 #include <folly/debugging/exception_tracer/StackTrace.h>
-#include <folly/experimental/symbolizer/Symbolizer.h>
 
 #if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF
 
@@ -47,13 +46,46 @@
 } // namespace
 
 // These functions are exported and may be found via dlsym(RTLD_NEXT, ...)
-extern "C" const StackTraceStack* getUncaughtExceptionStackTraceStack() {
+extern "C" const StackTraceStack*
+folly_exception_tracer_get_uncaught_exceptions_stack_trace_stack() {
   return invalid ? nullptr : &uncaughtExceptions;
 }
-extern "C" const StackTraceStack* getCaughtExceptionStackTraceStack() {
+extern "C" const StackTraceStack*
+folly_exception_tracer_get_caught_exceptions_stack_trace_stack() {
   return invalid ? nullptr : &caughtExceptions;
 }
 
+namespace folly {
+
+namespace detail {
+/// access_cxxabi
+///
+/// When building folly with thread sanitizer but using a prebuilt libstdc++ or
+/// libc++, folly's accesses here are instrumented while the standard library's
+/// accesses are not. This can cause false positive data-race reports. This
+/// namespace is here as an easy way to set up thread-sanitizer suppressions.
+///
+/// One scenario is when the __cxa_end_catch folly hook does an access, then the
+/// last std::exception_ptr to the same exception is released. The exception_ptr
+/// tor does an uninstrumented access and then calls free, which like malloc is
+/// instrumented. The thread-sanitizer runtime observes only an access in folly
+/// in one thread followed by a call to free in another thread, but does not
+/// observe the synchronizing refcount-decrement in between.
+///
+/// Aside from suppressions, there are two other solutions:
+/// * Never access cxxabi data directly.
+/// * Use an instrumented build of the standard library when building folly or
+///   the application with instrumentation.
+namespace access_cxxabi {
+
+static auto get_caught_exceptions_handler_count() {
+  return __cxxabiv1::__cxa_get_globals_fast()->caughtExceptions->handlerCount;
+}
+
+} // namespace access_cxxabi
+
+} // namespace detail
+
 namespace {
 
 void addActiveException() {
@@ -98,8 +130,8 @@
         return;
       }
 
-      __cxxabiv1::__cxa_exception* top =
-          __cxxabiv1::__cxa_get_globals_fast()->caughtExceptions;
+      auto topHandlerCount =
+          detail::access_cxxabi::get_caught_exceptions_handler_count();
       // This is gcc specific and not specified in the ABI:
       // abs(handlerCount) is the number of active handlers, it's negative
       // for rethrown exceptions and positive (always 1) for regular
@@ -107,7 +139,7 @@
       // In the rethrow case, we've already popped the exception off the
       // caught stack, so we don't do anything here.
       // For Lua interop, we see the handlerCount = 0
-      if ((top->handlerCount == 1) || (top->handlerCount == 0)) {
+      if ((topHandlerCount == 1) || (topHandlerCount == 0)) {
         if (!caughtExceptions.pop()) {
           uncaughtExceptions.clear();
           invalid = true;
@@ -129,6 +161,8 @@
 Initializer initializer;
 
 } // namespace
+
+} // namespace folly
 
 #endif //  FOLLY_HAS_EXCEPTION_TRACER
 
diff --git a/folly/folly/debugging/exception_tracer/ExceptionTracer.cpp b/folly/folly/debugging/exception_tracer/ExceptionTracer.cpp
--- a/folly/folly/debugging/exception_tracer/ExceptionTracer.cpp
+++ b/folly/folly/debugging/exception_tracer/ExceptionTracer.cpp
@@ -22,7 +22,6 @@
 
 #include <glog/logging.h>
 
-#include <folly/CppAttributes.h>
 #include <folly/Portability.h>
 #include <folly/String.h>
 #include <folly/debugging/exception_tracer/ExceptionAbi.h>
@@ -44,7 +43,8 @@
 using namespace __cxxabiv1;
 
 extern "C" {
-const StackTraceStack* getCaughtExceptionStackTraceStack(void)
+const StackTraceStack*
+folly_exception_tracer_get_caught_exceptions_stack_trace_stack(void)
     __attribute__((__weak__));
 typedef const StackTraceStack* (*GetCaughtExceptionStackTraceStackType)();
 GetCaughtExceptionStackTraceStackType getCaughtExceptionStackTraceStackFn;
@@ -138,14 +138,18 @@
 std::vector<ExceptionInfo> getCurrentExceptions() {
   struct Once {
     Once() {
-      // See if linked in with us (getCaughtExceptionStackTraceStack is weak)
-      getCaughtExceptionStackTraceStackFn = getCaughtExceptionStackTraceStack;
+      // See if linked in with us
+      // (folly_exception_tracer_get_caught_exceptions_stack_trace_stack is
+      // weak)
+      getCaughtExceptionStackTraceStackFn =
+          folly_exception_tracer_get_caught_exceptions_stack_trace_stack;
 
       if (!getCaughtExceptionStackTraceStackFn) {
         // Nope, see if it's in a shared library
         getCaughtExceptionStackTraceStackFn =
             (GetCaughtExceptionStackTraceStackType)dlsym(
-                RTLD_NEXT, "getCaughtExceptionStackTraceStack");
+                RTLD_NEXT,
+                "folly_exception_tracer_get_caught_exceptions_stack_trace_stack");
       }
     }
   };
diff --git a/folly/folly/debugging/exception_tracer/ExceptionTracerLib.cpp b/folly/folly/debugging/exception_tracer/ExceptionTracerLib.cpp
--- a/folly/folly/debugging/exception_tracer/ExceptionTracerLib.cpp
+++ b/folly/folly/debugging/exception_tracer/ExceptionTracerLib.cpp
@@ -32,7 +32,7 @@
 namespace __cxxabiv1 {
 
 extern "C" {
-#ifdef FOLLY_STATIC_LIBSTDCXX
+#ifdef FOLLY_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS
 [[noreturn]] void __real___cxa_throw(
     void* thrownException, std::type_info* type, void (*destructor)(void*));
 void* __real___cxa_begin_catch(void* excObj) noexcept;
@@ -49,7 +49,7 @@
 
 } // namespace __cxxabiv1
 
-#ifdef FOLLY_STATIC_LIBSTDCXX
+#ifdef FOLLY_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER
 extern "C" {
 #if __GLIBCXX__
 [[noreturn]] void
@@ -73,6 +73,10 @@
 class CallbackHolder {
  public:
   void registerCallback(Sig& f) { callbacks_.wlock()->push_back(f); }
+  void unregisterCallback(Sig& f) {
+    auto callbacks = callbacks_.wlock();
+    std::erase(*callbacks, f);
+  }
 
   // always inline to enforce kInternalFramesNumber
   template <typename... Args>
@@ -99,6 +103,9 @@
   }                                                             \
   void register##NAME##Callback(NAME##Sig& callback) {          \
     get##NAME##Callbacks().registerCallback(callback);          \
+  }                                                             \
+  void unregister##NAME##Callback(NAME##Sig& callback) {        \
+    get##NAME##Callbacks().unregisterCallback(callback);        \
   }
 
 FOLLY_EXNTRACE_DECLARE_CALLBACK(CxaThrow)
@@ -114,7 +121,7 @@
 
 namespace __cxxabiv1 {
 
-#ifdef FOLLY_STATIC_LIBSTDCXX
+#ifdef FOLLY_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS
 extern "C" {
 
 [[noreturn]] void __wrap___cxa_throw(
@@ -190,7 +197,7 @@
 
 } // namespace __cxxabiv1
 
-#ifdef FOLLY_STATIC_LIBSTDCXX
+#ifdef FOLLY_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER
 // Mangled name for std::rethrow_exception
 // TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr
 // is typedef'ed to a type in namespace __exception_ptr
diff --git a/folly/folly/debugging/exception_tracer/ExceptionTracerLib.h b/folly/folly/debugging/exception_tracer/ExceptionTracerLib.h
--- a/folly/folly/debugging/exception_tracer/ExceptionTracerLib.h
+++ b/folly/folly/debugging/exception_tracer/ExceptionTracerLib.h
@@ -37,6 +37,11 @@
 void registerCxaRethrowCallback(CxaRethrowSig& callback);
 void registerCxaEndCatchCallback(CxaEndCatchSig& callback);
 void registerRethrowExceptionCallback(RethrowExceptionSig& callback);
+void unregisterCxaThrowCallback(CxaThrowSig& callback);
+void unregisterCxaBeginCatchCallback(CxaBeginCatchSig& callback);
+void unregisterCxaRethrowCallback(CxaRethrowSig& callback);
+void unregisterCxaEndCatchCallback(CxaEndCatchSig& callback);
+void unregisterRethrowExceptionCallback(RethrowExceptionSig& callback);
 
 } // namespace exception_tracer
 } // namespace folly
diff --git a/folly/folly/debugging/exception_tracer/SmartExceptionStackTraceHooks.cpp b/folly/folly/debugging/exception_tracer/SmartExceptionStackTraceHooks.cpp
--- a/folly/folly/debugging/exception_tracer/SmartExceptionStackTraceHooks.cpp
+++ b/folly/folly/debugging/exception_tracer/SmartExceptionStackTraceHooks.cpp
@@ -27,15 +27,16 @@
 namespace {
 
 void metaDeleter(void* ex) noexcept {
-  auto syncMeta = detail::getMetaMap().withWLock([ex](auto& locked) noexcept {
+  auto meta = detail::getMetaMap().withWLock([ex](auto& locked) noexcept {
     auto iter = locked.find(ex);
+    CHECK(iter != locked.end());
     auto ret = std::move(iter->second);
     locked.erase(iter);
     return ret;
   });
 
   // If the thrown object was allocated statically it may not have a deleter.
-  auto meta = syncMeta->wlock();
+  CHECK(meta);
   if (meta->deleter) {
     meta->deleter(ex);
   }
@@ -57,31 +58,28 @@
   };
   handlingThrow = true;
 
-  // This can allocate memory potentially causing problems in an OOM
-  // situation so we catch and short circuit.
   try {
-    auto newMeta = std::make_unique<detail::SynchronizedExceptionMeta>();
-    newMeta->withWLock([&deleter](auto& lockedMeta) {
-      // Override the deleter with our custom one and capture the old one.
-      lockedMeta.deleter = std::exchange(*deleter, metaDeleter);
+    // This can allocate memory potentially causing problems in an OOM
+    // situation so we catch and short circuit.
+    auto meta = std::make_shared<detail::ExceptionMeta>();
 
-      ssize_t n = folly::symbolizer::getStackTrace(
-          lockedMeta.trace.addresses, kMaxFrames);
-      if (n != -1) {
-        lockedMeta.trace.frameCount = n;
-      }
-      ssize_t nAsync = folly::symbolizer::getAsyncStackTraceSafe(
-          lockedMeta.traceAsync.addresses, kMaxFrames);
-      if (nAsync != -1) {
-        lockedMeta.traceAsync.frameCount = nAsync;
-      }
-    });
+    // Override the deleter with our custom one and capture the old one.
+    meta->deleter = std::exchange(*deleter, metaDeleter);
 
-    auto oldMeta = detail::getMetaMap().withWLock([ex, &newMeta](auto& wlock) {
-      return std::exchange(wlock[ex], std::move(newMeta));
-    });
-    DCHECK(oldMeta == nullptr);
+    ssize_t n =
+        folly::symbolizer::getStackTrace(meta->trace.addresses, kMaxFrames);
+    if (n != -1) {
+      meta->trace.frameCount = n;
+    }
+    ssize_t nAsync = folly::symbolizer::getAsyncStackTraceSafe(
+        meta->traceAsync.addresses, kMaxFrames);
+    if (nAsync != -1) {
+      meta->traceAsync.frameCount = nAsync;
+    }
 
+    detail::getMetaMap().withWLock([&](auto& wlock) {
+      CHECK(wlock.try_emplace(ex, std::move(meta)).second);
+    });
   } catch (const std::bad_alloc&) {
   }
 }
diff --git a/folly/folly/debugging/exception_tracer/SmartExceptionTracer.cpp b/folly/folly/debugging/exception_tracer/SmartExceptionTracer.cpp
--- a/folly/folly/debugging/exception_tracer/SmartExceptionTracer.cpp
+++ b/folly/folly/debugging/exception_tracer/SmartExceptionTracer.cpp
@@ -24,7 +24,6 @@
 #include <folly/debugging/exception_tracer/ExceptionTracerLib.h>
 #include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h>
 #include <folly/debugging/exception_tracer/StackTrace.h>
-#include <folly/experimental/symbolizer/Symbolizer.h>
 #include <folly/lang/Exception.h>
 
 #if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF
@@ -42,7 +41,7 @@
 // the stack frames to use.
 template <typename ExceptionMetaFunc>
 ExceptionInfo getTraceWithFunc(
-    const std::exception& ex, ExceptionMetaFunc func) {
+    void* ex, const std::type_info* typeInfo, ExceptionMetaFunc func) {
   if (!detail::isSmartExceptionTracerHookEnabled() &&
       !loggedMessage.load(std::memory_order_relaxed)) {
     LOG(WARNING)
@@ -51,57 +50,49 @@
   }
 
   ExceptionInfo info;
-  info.type = &typeid(ex);
-  auto rlockedMeta = detail::getMetaMap().withRLock(
-      [&](const auto& locked) noexcept
-      -> detail::SynchronizedExceptionMeta::RLockedPtr {
-        auto* meta = get_ptr(locked, (void*)&ex);
-        // If we can't find the exception, return an empty stack trace.
-        if (!meta) {
-          return {};
-        }
-        CHECK(*meta);
-        // Acquire the meta rlock while holding the map's rlock, to block meta's
-        // destruction.
-        return (*meta)->rlock();
-      });
+  info.type = typeInfo;
 
-  if (!rlockedMeta) {
-    return info;
+  if (auto meta = get_default(*detail::getMetaMap().rlock(), ex)) {
+    auto [traceBeginIt, traceEndIt] = func(*meta);
+    info.frames.assign(traceBeginIt, traceEndIt);
   }
 
-  auto [traceBeginIt, traceEndIt] = func(*rlockedMeta);
-  info.frames.assign(traceBeginIt, traceEndIt);
   return info;
 }
 
 template <typename ExceptionMetaFunc>
 ExceptionInfo getTraceWithFunc(
     const std::exception_ptr& ptr, ExceptionMetaFunc func) {
-  if (auto* ex = folly::exception_ptr_get_object<std::exception>(ptr)) {
-    return getTraceWithFunc(*ex, std::move(func));
+  if (auto ex = folly::exception_ptr_get_object(ptr, nullptr)) {
+    return getTraceWithFunc(
+        ex, folly::exception_ptr_get_type(ptr), std::move(func));
   }
   return ExceptionInfo();
 }
 
 template <typename ExceptionMetaFunc>
 ExceptionInfo getTraceWithFunc(
+    const std::exception& e, ExceptionMetaFunc func) {
+  return getTraceWithFunc(
+      const_cast<void*>(dynamic_cast<const void*>(&e)),
+      &typeid(e),
+      std::move(func));
+}
+
+template <typename ExceptionMetaFunc>
+ExceptionInfo getTraceWithFunc(
     const exception_wrapper& ew, ExceptionMetaFunc func) {
-  if (auto* ex = ew.get_exception()) {
-    return getTraceWithFunc(*ex, std::move(func));
-  }
-  return ExceptionInfo();
+  return getTraceWithFunc(ew.exception_ptr(), std::move(func));
 }
 
 auto getAsyncStackTraceItPair(const detail::ExceptionMeta& meta) {
-  return std::make_pair(
-      meta.traceAsync.addresses,
-      meta.traceAsync.addresses + meta.traceAsync.frameCount);
+  auto addr = meta.traceAsync.addresses;
+  return std::pair(addr, addr + meta.traceAsync.frameCount);
 }
 
 auto getNormalStackTraceItPair(const detail::ExceptionMeta& meta) {
-  return std::make_pair(
-      meta.trace.addresses, meta.trace.addresses + meta.trace.frameCount);
+  auto addr = meta.trace.addresses;
+  return std::pair(addr, addr + meta.trace.frameCount);
 }
 
 } // namespace
diff --git a/folly/folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h b/folly/folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h
--- a/folly/folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h
+++ b/folly/folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h
@@ -30,10 +30,11 @@
   StackTrace traceAsync;
 };
 
-using SynchronizedExceptionMeta = folly::Synchronized<ExceptionMeta>;
-
+// using a vector-map so that all the values are laid out contiguously in an
+// array, specifically to make debugging easier - no need to learn the structure
+// of the f14-chunk-array when debugging the exception hooks
 using ExceptionMetaMap =
-    folly::F14VectorMap<void*, std::unique_ptr<SynchronizedExceptionMeta>>;
+    folly::F14VectorMap<void const*, std::shared_ptr<ExceptionMeta const>>;
 
 Synchronized<ExceptionMetaMap>& getMetaMap();
 
diff --git a/folly/folly/debugging/symbolizer/Dwarf.cpp b/folly/folly/debugging/symbolizer/Dwarf.cpp
--- a/folly/folly/debugging/symbolizer/Dwarf.cpp
+++ b/folly/folly/debugging/symbolizer/Dwarf.cpp
@@ -27,7 +27,7 @@
 
 #if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF
 
-#include <dwarf.h> // @manual=fbsource//third-party/libdwarf:dwarf
+#include <dwarf.h> // @manual
 
 namespace folly {
 namespace symbolizer {
diff --git a/folly/folly/debugging/symbolizer/Dwarf.h b/folly/folly/debugging/symbolizer/Dwarf.h
--- a/folly/folly/debugging/symbolizer/Dwarf.h
+++ b/folly/folly/debugging/symbolizer/Dwarf.h
@@ -21,9 +21,9 @@
 #include <folly/Function.h>
 #include <folly/Range.h>
 #include <folly/debugging/symbolizer/DwarfUtil.h>
-#include <folly/experimental/symbolizer/Elf.h>
-#include <folly/experimental/symbolizer/ElfCache.h>
-#include <folly/experimental/symbolizer/SymbolizedFrame.h>
+#include <folly/debugging/symbolizer/Elf.h>
+#include <folly/debugging/symbolizer/ElfCache.h>
+#include <folly/debugging/symbolizer/SymbolizedFrame.h>
 
 namespace folly {
 namespace symbolizer {
diff --git a/folly/folly/debugging/symbolizer/DwarfImpl.cpp b/folly/folly/debugging/symbolizer/DwarfImpl.cpp
--- a/folly/folly/debugging/symbolizer/DwarfImpl.cpp
+++ b/folly/folly/debugging/symbolizer/DwarfImpl.cpp
@@ -27,7 +27,7 @@
 
 #if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF
 
-#include <dwarf.h> // @manual=fbsource//third-party/libdwarf:dwarf
+#include <dwarf.h> // @manual
 
 // We need a single dwarf5 tag, but may not be building against
 // a new enough libdwarf, so just define it ourselves.
diff --git a/folly/folly/debugging/symbolizer/DwarfImpl.h b/folly/folly/debugging/symbolizer/DwarfImpl.h
--- a/folly/folly/debugging/symbolizer/DwarfImpl.h
+++ b/folly/folly/debugging/symbolizer/DwarfImpl.h
@@ -25,9 +25,9 @@
 #include <folly/debugging/symbolizer/DwarfLineNumberVM.h>
 #include <folly/debugging/symbolizer/DwarfSection.h>
 #include <folly/debugging/symbolizer/DwarfUtil.h>
-#include <folly/experimental/symbolizer/Elf.h>
-#include <folly/experimental/symbolizer/ElfCache.h>
-#include <folly/experimental/symbolizer/SymbolizedFrame.h>
+#include <folly/debugging/symbolizer/Elf.h>
+#include <folly/debugging/symbolizer/ElfCache.h>
+#include <folly/debugging/symbolizer/SymbolizedFrame.h>
 
 namespace folly {
 namespace symbolizer {
diff --git a/folly/folly/debugging/symbolizer/DwarfLineNumberVM.h b/folly/folly/debugging/symbolizer/DwarfLineNumberVM.h
--- a/folly/folly/debugging/symbolizer/DwarfLineNumberVM.h
+++ b/folly/folly/debugging/symbolizer/DwarfLineNumberVM.h
@@ -18,7 +18,7 @@
 
 #include <folly/Range.h>
 #include <folly/debugging/symbolizer/DwarfUtil.h>
-#include <folly/experimental/symbolizer/SymbolizedFrame.h>
+#include <folly/debugging/symbolizer/SymbolizedFrame.h>
 
 namespace folly {
 namespace symbolizer {
diff --git a/folly/folly/debugging/symbolizer/DwarfUtil.cpp b/folly/folly/debugging/symbolizer/DwarfUtil.cpp
--- a/folly/folly/debugging/symbolizer/DwarfUtil.cpp
+++ b/folly/folly/debugging/symbolizer/DwarfUtil.cpp
@@ -21,13 +21,13 @@
 
 #include <folly/Optional.h>
 #include <folly/Range.h>
-#include <folly/experimental/symbolizer/Elf.h>
+#include <folly/debugging/symbolizer/Elf.h>
 #include <folly/portability/Config.h>
 #include <folly/portability/Unistd.h>
 
 #if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF
 
-#include <dwarf.h> // @manual=fbsource//third-party/libdwarf:dwarf
+#include <dwarf.h> // @manual
 
 // We need a single dwarf5 tag, but may not be building against
 // a new enough libdwarf, so just define it ourselves.
diff --git a/folly/folly/debugging/symbolizer/DwarfUtil.h b/folly/folly/debugging/symbolizer/DwarfUtil.h
--- a/folly/folly/debugging/symbolizer/DwarfUtil.h
+++ b/folly/folly/debugging/symbolizer/DwarfUtil.h
@@ -22,11 +22,11 @@
 
 #include <folly/Function.h>
 #include <folly/Range.h>
-#include <folly/experimental/symbolizer/Elf.h>
-#include <folly/experimental/symbolizer/ElfCache.h>
+#include <folly/debugging/symbolizer/Elf.h>
+#include <folly/debugging/symbolizer/ElfCache.h>
 
 #if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF
-#include <dwarf.h> // @manual=fbsource//third-party/libdwarf:dwarf
+#include <dwarf.h> // @manual
 
 namespace folly {
 namespace symbolizer {
diff --git a/folly/folly/debugging/symbolizer/Elf-inl.h b/folly/folly/debugging/symbolizer/Elf-inl.h
--- a/folly/folly/debugging/symbolizer/Elf-inl.h
+++ b/folly/folly/debugging/symbolizer/Elf-inl.h
@@ -19,11 +19,20 @@
 
 template <class Fn>
 const ElfPhdr* ElfFile::iterateProgramHeaders(Fn fn) const
-    noexcept(is_nothrow_invocable_v<Fn, ElfPhdr const&>) {
-  // there exist ELF binaries which execute correctly, but have invalid internal
-  // offset(s) to program/section headers; most probably due to invalid
-  // stripping of symbols
-  if (elfHeader().e_phoff + sizeof(ElfPhdr) >= length_) {
+    noexcept(is_nothrow_invocable_v<Fn&, ElfPhdr const&>) {
+  // Validate that all program headers fit within the mapped file bounds.
+  // There exist malformed ELF binaries with invalid header counts/offsets,
+  // which can cause SIGBUS when iterating past the valid mapped memory
+  // region. This code must handle malformed binaries without crashing.
+  size_t headersSize;
+  size_t headersEnd;
+  if (!folly::checked_mul(
+          &headersSize,
+          static_cast<size_t>(elfHeader().e_phnum),
+          sizeof(ElfPhdr)) ||
+      !folly::checked_add(
+          &headersEnd, static_cast<size_t>(elfHeader().e_phoff), headersSize) ||
+      headersEnd > length_) {
     return nullptr;
   }
 
@@ -38,11 +47,22 @@
 
 template <class Fn>
 const ElfShdr* ElfFile::iterateSections(Fn fn) const
-    noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>) {
-  // there exist ELF binaries which execute correctly, but have invalid internal
-  // offset(s) to program/section headers; most probably due to invalid
-  // stripping of symbols
-  if (elfHeader().e_shoff + sizeof(ElfShdr) >= length_) {
+    noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>) {
+  // Validate that all section headers fit within the mapped file bounds.
+  // There exist malformed ELF binaries with invalid header counts/offsets,
+  // which can cause SIGBUS when iterating past the valid mapped memory
+  // region. This code must handle malformed binaries without crashing.
+  size_t sectionsSize;
+  size_t sectionsEnd;
+  if (!folly::checked_mul(
+          &sectionsSize,
+          static_cast<size_t>(elfHeader().e_shnum),
+          sizeof(ElfShdr)) ||
+      !folly::checked_add(
+          &sectionsEnd,
+          static_cast<size_t>(elfHeader().e_shoff),
+          sectionsSize) ||
+      sectionsEnd > length_) {
     return nullptr;
   }
 
@@ -57,7 +77,7 @@
 
 template <class Fn>
 const ElfShdr* ElfFile::iterateSectionsWithType(uint32_t type, Fn fn) const
-    noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>) {
+    noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>) {
   return iterateSections([&](const ElfShdr& sh) {
     return sh.sh_type == type && fn(sh);
   });
@@ -66,7 +86,7 @@
 template <class Fn>
 const ElfShdr* ElfFile::iterateSectionsWithTypes(
     std::initializer_list<uint32_t> types, Fn fn) const
-    noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>) {
+    noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>) {
   return iterateSections([&](const ElfShdr& sh) {
     auto const it = std::find(types.begin(), types.end(), sh.sh_type);
     return it != types.end() && fn(sh);
@@ -75,7 +95,7 @@
 
 template <class Fn>
 const char* ElfFile::iterateStrings(const ElfShdr& stringTable, Fn fn) const
-    noexcept(is_nothrow_invocable_v<Fn, const char*>) {
+    noexcept(is_nothrow_invocable_v<Fn&, const char*>) {
   validateStringTable(stringTable);
 
   const char* start = file_ + stringTable.sh_offset;
@@ -96,6 +116,19 @@
   FOLLY_SAFE_CHECK(
       section.sh_entsize == sizeof(E), "invalid entry size in table");
 
+  // Validate that the entire section data is within the mapped file bounds.
+  // There exist malformed ELF binaries with invalid section offsets/sizes,
+  // which can cause SIGBUS when iterating past the valid mapped memory
+  // region. This code must handle malformed binaries without crashing.
+  size_t sectionEnd;
+  if (!folly::checked_add(
+          &sectionEnd,
+          static_cast<size_t>(section.sh_offset),
+          static_cast<size_t>(section.sh_size)) ||
+      sectionEnd > length_) {
+    return nullptr;
+  }
+
   const E* ent = &at<E>(section.sh_offset);
   const E* end = ent + (section.sh_size / section.sh_entsize);
 
@@ -112,14 +145,14 @@
 
 template <class Fn>
 const ElfSym* ElfFile::iterateSymbols(const ElfShdr& section, Fn fn) const
-    noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>) {
+    noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>) {
   return iterateSectionEntries<ElfSym>(section, fn);
 }
 
 template <class Fn>
 const ElfSym* ElfFile::iterateSymbolsWithType(
     const ElfShdr& section, uint32_t type, Fn fn) const
-    noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>) {
+    noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>) {
   // N.B. st_info has the same representation on 32- and 64-bit platforms
   return iterateSymbols(section, [&](const ElfSym& sym) -> bool {
     return ELF32_ST_TYPE(sym.st_info) == type && fn(sym);
@@ -129,13 +162,100 @@
 template <class Fn>
 const ElfSym* ElfFile::iterateSymbolsWithTypes(
     const ElfShdr& section, std::initializer_list<uint32_t> types, Fn fn) const
-    noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>) {
+    noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>) {
   // N.B. st_info has the same representation on 32- and 64-bit platforms
   return iterateSymbols(section, [&](const ElfSym& sym) -> bool {
     auto const elfType = ELF32_ST_TYPE(sym.st_info);
     auto const it = std::find(types.begin(), types.end(), elfType);
     return it != types.end() && fn(sym);
   });
+}
+
+template <class Fn>
+folly::Expected<ElfFile::Note, ElfFile::FindNoteError>
+ElfFile::iterateNotesInBodyHelper(folly::StringPiece body, Fn& fn) const
+    noexcept(is_nothrow_invocable_v<Fn&, const Note&>) {
+  static_assert(alignof(ElfNhdr) >= 4);
+  if (uintptr_t(body.data()) % alignof(ElfNhdr) != 0) {
+    return Unexpected(FindNoteError(FindNoteFailureCode::NoteUnaligned));
+  }
+
+  while (body.size() > 0) {
+    folly::span<const uint8_t> noteBody =
+        span(reinterpret_cast<const uint8_t*>(body.data()), body.size());
+    auto noteMaybe = Note::parse(noteBody);
+    if (!noteMaybe) {
+      return noteMaybe;
+    }
+
+    auto note = *noteMaybe;
+
+    if (fn(note)) {
+      return note;
+    }
+
+    body.advance(note.alignedSize());
+  }
+
+  return Unexpected(FindNoteError(FindNoteFailureCode::NoteNotFound));
+}
+
+template <class Fn>
+folly::Expected<ElfFile::Note, ElfFile::FindNoteError>
+ElfFile::iterateNotesInSections(const ElfShdr* section, Fn fn) const
+    noexcept(is_nothrow_invocable_v<Fn&, const Note&>) {
+  if (section != nullptr) {
+    return iterateNotesInBodyHelper(getSectionBody(*section), fn);
+  }
+
+  folly::Expected<Note, FindNoteError> noteMaybe =
+      Unexpected(FindNoteError(FindNoteFailureCode::NoteNotFound));
+  iterateSectionsWithType(SHT_NOTE, [&](const ElfShdr& sh) {
+    noteMaybe = iterateNotesInBodyHelper(getSectionBody(sh), fn);
+    // Check if we got a good result, if so return it
+    if (noteMaybe) {
+      return STOP;
+    }
+
+    // Check if we got a data corruption error, stop and return that.
+    if (noteMaybe.error().isDataCorruptionError()) {
+      return STOP;
+    }
+    return CONTINUE;
+  });
+
+  return noteMaybe;
+}
+
+template <class Fn>
+folly::Expected<ElfFile::Note, ElfFile::FindNoteError>
+ElfFile::iterateNotesInSegments(const ElfPhdr* segment, Fn fn) const
+    noexcept(is_nothrow_invocable_v<Fn&, const Note&>) {
+  if (segment != nullptr) {
+    return iterateNotesInBodyHelper(getSegmentBody(*segment), fn);
+  }
+
+  folly::Expected<Note, FindNoteError> noteMaybe =
+      Unexpected(FindNoteError(FindNoteFailureCode::NoteNotFound));
+  iterateProgramHeaders([&](const ElfPhdr& ph) {
+    if (ph.p_type != PT_NOTE) {
+      return CONTINUE;
+    }
+
+    noteMaybe = iterateNotesInBodyHelper(getSegmentBody(ph), fn);
+    // Check if we got a good result, if so return it
+    if (noteMaybe) {
+      return STOP;
+    }
+
+    // Check if we got a data corruption error, stop and return that.
+    if (noteMaybe.error().isDataCorruptionError()) {
+      return STOP;
+    }
+    return CONTINUE;
+  });
+
+  return noteMaybe;
 }
 
 } // namespace symbolizer
diff --git a/folly/folly/debugging/symbolizer/Elf.cpp b/folly/folly/debugging/symbolizer/Elf.cpp
--- a/folly/folly/debugging/symbolizer/Elf.cpp
+++ b/folly/folly/debugging/symbolizer/Elf.cpp
@@ -18,9 +18,7 @@
 
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <cstring>
-#include <string>
 
 #include <glog/logging.h>
 
@@ -96,7 +94,11 @@
   }
 
   uint64_t mtime_ns = st.st_mtim.tv_sec * 1000'000'000LL + st.st_mtim.tv_nsec;
-  fileId_ = ElfFileId{st.st_dev, st.st_ino, st.st_size, mtime_ns};
+  fileId_ = ElfFileId{
+      to_narrow(st.st_dev),
+      to_narrow(st.st_ino),
+      to_narrow(st.st_size),
+      mtime_ns};
 
   length_ = st.st_size;
   int prot = PROT_READ;
@@ -222,12 +224,12 @@
     return {kInvalidElfFile, "not an ELF file (too short)"};
   }
 
-  std::array<char, 5> elfMagBuf = {{0, 0, 0, 0, 0}};
+  std::array<char, SELFMAG> elfMagBuf = {{0, 0, 0, 0}};
   if (::lseek(fd_, 0, SEEK_SET) != 0 ||
-      fileops::read(fd_, elfMagBuf.data(), 4) != 4) {
+      fileops::read(fd_, elfMagBuf.data(), SELFMAG) != SELFMAG) {
     return {kInvalidElfFile, "unable to read ELF file for magic number"};
   }
-  if (std::strncmp(elfMagBuf.data(), ELFMAG, sizeof(ELFMAG)) != 0) {
+  if (std::strncmp(elfMagBuf.data(), ELFMAG, SELFMAG) != 0) {
     return {kInvalidElfFile, "invalid ELF magic"};
   }
   char c;
@@ -279,7 +281,7 @@
 
   // We support executable and shared object files and extracting debug info
   // from relocatable objects (.dwo sections in .o/.dwo files). The e_phnum and
-  // e_phentsize header fileds are not required for relocatable files.
+  // e_phentsize header fields are not required for relocatable files.
   // https://docs.oracle.com/cd/E19620-01/805-4693/6j4emccrq/index.html
   if (elfHeader.e_type != ET_REL) {
     if (elfHeader.e_phnum == 0) {
@@ -327,6 +329,11 @@
   return folly::StringPiece(file_ + section.sh_offset, section.sh_size);
 }
 
+folly::StringPiece ElfFile::getSegmentBody(
+    const ElfPhdr& segment) const noexcept {
+  return folly::StringPiece(file_ + segment.p_offset, segment.p_filesz);
+}
+
 void ElfFile::validateStringTable(const ElfShdr& stringTable) const noexcept {
   FOLLY_SAFE_CHECK(
       stringTable.sh_type == SHT_STRTAB, "invalid type for string table");
@@ -480,23 +487,47 @@
   return getString(*linkSection, symbol.second->st_name);
 }
 
-std::pair<const int, char const*> ElfFile::posixFadvise(
-    off_t offset, off_t len, int const advice) const noexcept {
-  if (fd_ == -1) {
-    return {1, "file not open"};
-  }
-  int res = posix_fadvise(fd_, offset, len, advice);
-  if (res != 0) {
-    return {res, "posix_fadvise failed for file"};
-  }
-  return {res, ""};
+folly::Expected<span<const uint8_t>, ElfFile::FindNoteError>
+ElfFile::getNoteGnuBuildId() const noexcept {
+  auto filter = [](const Note& note) {
+    return note.getName() == "GNU" && note.getType() == NT_GNU_BUILD_ID;
+  };
+  auto desc = [](auto note) { return note.getDesc(); };
+  auto section = getSectionByName(".note.gnu.build-id");
+  return iterateNotesInSections(section, filter).then(desc);
 }
 
-std::pair<const int, char const*> ElfFile::posixFadvise(
-    int const advice) const noexcept {
-  return posixFadvise(0, 0, advice);
+folly::Expected<ElfFile::Note, ElfFile::FindNoteError> ElfFile::findNoteByName(
+    std::string_view name) const noexcept {
+  auto filter = [name](const Note& note) { return note.getName() == name; };
+
+  // If we get a success, or a data corruption error, return it.
+  // otherwise iterate segments.
+  auto foundMaybe = iterateNotesInSections(nullptr, filter);
+  if (foundMaybe || foundMaybe.error().isDataCorruptionError()) {
+    return foundMaybe;
+  }
+
+  foundMaybe = iterateNotesInSegments(nullptr, filter);
+  return foundMaybe;
 }
 
+folly::Expected<ElfFile::Note, ElfFile::FindNoteError> ElfFile::findNoteByType(
+    size_t type) const noexcept {
+  auto filter = [type](const Note& note) {
+    return note.header()->n_type == type;
+  };
+
+  // If we get a success, or a data corruption error, return it.
+  // otherwise iterate segments.
+  auto foundMaybe = iterateNotesInSections(nullptr, filter);
+  if (foundMaybe || foundMaybe.error().isDataCorruptionError()) {
+    return foundMaybe;
+  }
+
+  foundMaybe = iterateNotesInSegments(nullptr, filter);
+  return foundMaybe;
+}
 } // namespace symbolizer
 } // namespace folly
 
diff --git a/folly/folly/debugging/symbolizer/Elf.h b/folly/folly/debugging/symbolizer/Elf.h
--- a/folly/folly/debugging/symbolizer/Elf.h
+++ b/folly/folly/debugging/symbolizer/Elf.h
@@ -29,7 +29,9 @@
 #include <folly/Conv.h>
 #include <folly/Likely.h>
 #include <folly/Range.h>
+#include <folly/container/span.h>
 #include <folly/lang/SafeAssert.h>
+#include <folly/lang/cstring_view.h>
 #include <folly/portability/Config.h>
 
 #if FOLLY_HAVE_ELF
@@ -49,12 +51,14 @@
 using ElfAddr = FOLLY_ELF_ELFW(Addr);
 using ElfEhdr = FOLLY_ELF_ELFW(Ehdr);
 using ElfOff = FOLLY_ELF_ELFW(Off);
+using ElfWord = FOLLY_ELF_ELFW(Word);
 using ElfPhdr = FOLLY_ELF_ELFW(Phdr);
 using ElfShdr = FOLLY_ELF_ELFW(Shdr);
 using ElfSym = FOLLY_ELF_ELFW(Sym);
 using ElfRel = FOLLY_ELF_ELFW(Rel);
 using ElfRela = FOLLY_ELF_ELFW(Rela);
 using ElfDyn = FOLLY_ELF_ELFW(Dyn);
+using ElfNhdr = FOLLY_ELF_ELFW(Nhdr);
 
 // ElfFileId is supposed to uniquely identify any instance of an ELF binary.
 // It does that by using the file's inode, dev ID, size and modification time
@@ -158,6 +162,8 @@
   /** Get the actual section body */
   folly::StringPiece getSectionBody(const ElfShdr& section) const noexcept;
 
+  folly::StringPiece getSegmentBody(const ElfPhdr& segment) const noexcept;
+
   /** Retrieve a string from a string table section */
   const char* getString(
       const ElfShdr& stringTable, size_t offset) const noexcept;
@@ -170,7 +176,7 @@
    */
   template <class Fn>
   const char* iterateStrings(const ElfShdr& stringTable, Fn fn) const
-      noexcept(is_nothrow_invocable_v<Fn, const char*>);
+      noexcept(is_nothrow_invocable_v<Fn&, const char*>);
 
   /**
    * Iterate over program headers as long as fn(section) returns false.
@@ -179,7 +185,7 @@
    */
   template <class Fn>
   const ElfPhdr* iterateProgramHeaders(Fn fn) const
-      noexcept(is_nothrow_invocable_v<Fn, ElfPhdr const&>);
+      noexcept(is_nothrow_invocable_v<Fn&, ElfPhdr const&>);
 
   /**
    * Iterate over all sections for as long as fn(section) returns false.
@@ -188,7 +194,7 @@
    */
   template <class Fn>
   const ElfShdr* iterateSections(Fn fn) const
-      noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>);
+      noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>);
 
   /**
    * Iterate over all sections with a given type. Similar to
@@ -196,7 +202,7 @@
    */
   template <class Fn>
   const ElfShdr* iterateSectionsWithType(uint32_t type, Fn fn) const
-      noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>);
+      noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>);
 
   /**
    * Iterate over all sections with a given types. Similar to
@@ -205,7 +211,7 @@
   template <class Fn>
   const ElfShdr* iterateSectionsWithTypes(
       std::initializer_list<uint32_t> types, Fn fn) const
-      noexcept(is_nothrow_invocable_v<Fn, ElfShdr const&>);
+      noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>);
 
   /**
    * Iterate over all symbols within a given section.
@@ -215,16 +221,16 @@
    */
   template <class Fn>
   const ElfSym* iterateSymbols(const ElfShdr& section, Fn fn) const
-      noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>);
+      noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>);
   template <class Fn>
   const ElfSym* iterateSymbolsWithType(
       const ElfShdr& section, uint32_t type, Fn fn) const
-      noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>);
+      noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>);
   template <class Fn>
   const ElfSym* iterateSymbolsWithTypes(
       const ElfShdr& section,
       std::initializer_list<uint32_t> types,
-      Fn fn) const noexcept(is_nothrow_invocable_v<Fn, ElfSym const&>);
+      Fn fn) const noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>);
 
   /**
    * Iterate over entries within a given section.
@@ -244,7 +250,7 @@
    *
    * Returns {nullptr, nullptr} if not found.
    */
-  typedef std::pair<const ElfShdr*, const ElfSym*> Symbol;
+  using Symbol = std::pair<const ElfShdr*, const ElfSym*>;
   Symbol getDefinitionByAddress(uintptr_t address) const noexcept;
 
   /**
@@ -360,21 +366,187 @@
 
   const ElfFileId& getFileId() const { return fileId_; }
 
+  enum class FindNoteFailureCode {
+    NoteNotFound = 1,
+    NoteUndersized,
+    NoteUnaligned,
+  };
+
+  /** Describes the reason searching for a note has failed, including an error
+   * code and a flag denoting if that error code is due to corruption in the ELF
+   * file itself.
+   */
+  struct FindNoteError {
+    FindNoteFailureCode failureCode;
+
+    static inline constexpr std::string_view GetNoteErrorString[] = {
+        "Note not found",
+        "Note body undersized (smaller than specified in the note header)",
+        "Note underaligned (aligned less than the note header)",
+    };
+
+    static constexpr std::string_view getErrorMessage(
+        FindNoteError& err) noexcept {
+      auto index = to_underlying(err.failureCode);
+      if (index < 1 || to_unsigned(index) > std::size(GetNoteErrorString)) {
+        return std::string_view{};
+      }
+
+      return GetNoteErrorString[index - 1];
+    }
+
+    bool isDataCorruptionError() {
+      return failureCode == FindNoteFailureCode::NoteUndersized ||
+          failureCode == FindNoteFailureCode::NoteUnaligned;
+    }
+
+    explicit FindNoteError(FindNoteFailureCode code) : failureCode(code) {}
+  };
+
+  /** Structure containing a note header and it's body */
+  struct Note {
+    folly::span<const uint8_t> note;
+    explicit Note(folly::span<const uint8_t> note_) : note(note_) {}
+
+    const ElfNhdr* header() const {
+      return reinterpret_cast<const ElfNhdr*>(note.data());
+    }
+
+    folly::span<const uint8_t> body() const {
+      return note.subspan(sizeof(*header()));
+    }
+
+    ElfWord getType() const {
+      if (!header()) {
+        return {};
+      }
+      return header()->n_type;
+    }
+
+    cstring_view getName() const {
+      if (!header()) {
+        return {};
+      }
+
+      // Subtract 1 to remove the trailing null character.
+      return cstring_view(
+          reinterpret_cast<const char*>(body().data()), header()->n_namesz - 1);
+    }
+
+    folly::span<const uint8_t> getDesc() const {
+      if (!header()) {
+        return span<const uint8_t>{};
+      }
+
+      size_t paddedNameSize = folly::align_ceil(header()->n_namesz, 4);
+      return body().subspan(paddedNameSize);
+    }
+
+    static size_t alignedBodySize(const ElfNhdr& nhdr) {
+      return folly::align_ceil(nhdr.n_namesz, 4) +
+          folly::align_ceil(nhdr.n_descsz, 4);
+    }
+
+    static size_t alignedSize(const ElfNhdr& nhdr) {
+      return sizeof(ElfNhdr) + alignedBodySize(nhdr);
+    }
+
+    /**
+     * Get the aligned size of this note. If the header is null, 0 will be
+     * returned.
+     */
+    size_t alignedSize() const { return header() ? alignedSize(*header()) : 0; }
+
+    /**
+     * Parse a note from a given set of bytes. If the supplied bytes is less
+     * than the note header, or the aligned size of the body an unexpected will
+     * be returned.
+     */
+    static folly::Expected<Note, FindNoteError> parse(
+        folly::span<const uint8_t> noteBody) {
+      if (noteBody.size() < sizeof(ElfNhdr)) {
+        return Unexpected(FindNoteError(FindNoteFailureCode::NoteUndersized));
+      }
+
+      const ElfNhdr* nhdr = reinterpret_cast<const ElfNhdr*>(noteBody.data());
+      if (alignedSize(*nhdr) > noteBody.size()) {
+        return Unexpected(FindNoteError(FindNoteFailureCode::NoteUndersized));
+      }
+
+      return Note(noteBody.subspan(0, alignedSize(*nhdr)));
+    }
+  };
+
   /**
-   * Announce an intention to access file data in a specific pattern in the
-   * future. https://man7.org/linux/man-pages/man2/posix_fadvise.2.html
+   * Iterate over notes in a given section, or all sections if section is null.
+   * In the case no note is found, a pair will be returned with a nullptr for
+   * the note header and an empty span for the note body.
+   *
+   * Note that notes are not unique, and notes in sections may also
+   * be present when iterating over segments.
    */
-  std::pair<const int, char const*> posixFadvise(
-      off_t offset, off_t len, int const advice) const noexcept;
-  std::pair<const int, char const*> posixFadvise(
-      int const advice) const noexcept;
+  template <class Fn>
+  folly::Expected<Note, FindNoteError> iterateNotesInSections(
+      const ElfShdr* section, Fn fn) const
+      noexcept(is_nothrow_invocable_v<Fn&, const Note&>);
 
+  /**
+   * Iterate over notes in a given segment, or all segments if segment is null.
+   * In the case no note is found, a pair will be returned with a nullptr for
+   * the note header and an empty span for the note body.
+   *
+   * Note that notes are not unique, and notes in segments may also
+   * be present when iterating over sections.
+   */
+  template <class Fn>
+  folly::Expected<Note, FindNoteError> iterateNotesInSegments(
+      const ElfPhdr* segment, Fn fn) const
+      noexcept(is_nothrow_invocable_v<Fn&, const Note&>);
+
+  /**
+   * Retrieve the content of .note.gnu.build-id, if available.
+   */
+  folly::Expected<span<const uint8_t>, FindNoteError> getNoteGnuBuildId()
+      const noexcept;
+
+  /**
+   * Find a note by name in either a section or segment. If multiple
+   * notes share the same name, the first match will be returned. An error will
+   * be returned if the note is not found, or if there is underlying file
+   * corruption.
+   *
+   * Empty is a valid input, and will return when notes with an empty name are
+   * found. This can happen in ELF Cores, or other notes when the namespace
+   * unknown. Read more at https://man7.org/linux/man-pages/man5/elf.5.html
+   *
+   * Note that notes are not unique, and notes in sections may also be in
+   * segments. For this reason this method does not differentiate between
+   * segments and sections.
+   */
+  folly::Expected<Note, FindNoteError> findNoteByName(
+      std::string_view name) const noexcept;
+
+  /**
+   * Find a note by type in either a section or segment. If multiple
+   * notes share the same type, the first match will be returned. An error will
+   * be returned if the note is not found, or if there is underlying file
+   * corruption.
+   *
+   * Note that notes are not unique, and notes in sections may also be in
+   * segments. For this reason this method does not differentiate between
+   * segments and sections.
+   */
+  folly::Expected<Note, FindNoteError> findNoteByType(
+      size_t type) const noexcept;
+
  private:
   OpenResult init() noexcept;
   void reset() noexcept;
   ElfFile(const ElfFile&) = delete;
   ElfFile& operator=(const ElfFile&) = delete;
 
+  enum { CONTINUE = 0, STOP = 1 };
+
   void validateStringTable(const ElfShdr& stringTable) const noexcept;
 
   template <class T>
@@ -428,6 +600,14 @@
 
     return (offset + sizeof(T) <= length_) ? &at<T>(offset) : nullptr;
   }
+
+  // Helper to iterate notes in a section or segment.
+  // Takes in a StringPiece of the body of the section or segment, and iterates
+  // over the notes in that body.
+  template <class Fn>
+  folly::Expected<Note, FindNoteError> iterateNotesInBodyHelper(
+      folly::StringPiece body, Fn& fn) const
+      noexcept(is_nothrow_invocable_v<Fn&, const Note&>);
 
   static constexpr size_t kFilepathMaxLen = 512;
   char filepath_[kFilepathMaxLen] = {};
diff --git a/folly/folly/debugging/symbolizer/ElfCache.h b/folly/folly/debugging/symbolizer/ElfCache.h
--- a/folly/folly/debugging/symbolizer/ElfCache.h
+++ b/folly/folly/debugging/symbolizer/ElfCache.h
@@ -27,7 +27,7 @@
 
 #include <folly/Optional.h>
 #include <folly/Range.h>
-#include <folly/experimental/symbolizer/Elf.h>
+#include <folly/debugging/symbolizer/Elf.h>
 #include <folly/hash/Hash.h>
 #include <folly/memory/ReentrantAllocator.h>
 #include <folly/portability/Config.h>
@@ -40,7 +40,12 @@
 class ElfCacheBase {
  public:
   virtual std::shared_ptr<ElfFile> getFile(StringPiece path) = 0;
-  virtual ~ElfCacheBase() {}
+  virtual ~ElfCacheBase() = default;
+  ElfCacheBase() = default;
+  ElfCacheBase(const ElfCacheBase&) = delete;
+  ElfCacheBase& operator=(const ElfCacheBase&) = delete;
+  ElfCacheBase(ElfCacheBase&&) = delete;
+  ElfCacheBase& operator=(ElfCacheBase&&) = delete;
 };
 
 /**
@@ -69,9 +74,12 @@
         char const* data,
         std::size_t size,
         reentrant_allocator<char> const& alloc) noexcept;
+    ~Path() = default;
     Path() = delete;
     Path(Path const&) = delete;
     void operator=(Path const&) = delete;
+    Path(Path&&) = delete;
+    Path& operator=(Path&&) = delete;
 
     /* implicit */ operator StringPiece() const noexcept { return data_; }
 
@@ -93,8 +101,11 @@
     explicit Entry(StringPiece p, reentrant_allocator<char> alloc) noexcept
         : path{p.data(), p.size(), alloc},
           file{std::allocate_shared<ElfFile>(alloc)} {}
+    ~Entry() = default;
     Entry(Entry const&) = delete;
     Entry& operator=(Entry const& that) = delete;
+    Entry(Entry&&) = delete;
+    Entry& operator=(Entry&&) = delete;
 
     friend bool operator<(Entry const& a, Entry const& b) noexcept {
       return a.path < b.path;
diff --git a/folly/folly/debugging/symbolizer/SignalHandler.cpp b/folly/folly/debugging/symbolizer/SignalHandler.cpp
--- a/folly/folly/debugging/symbolizer/SignalHandler.cpp
+++ b/folly/folly/debugging/symbolizer/SignalHandler.cpp
@@ -21,7 +21,6 @@
 #include <signal.h>
 #include <sys/types.h>
 
-#include <algorithm>
 #include <atomic>
 #include <cerrno>
 #include <ctime>
@@ -31,7 +30,7 @@
 #include <glog/logging.h>
 
 #include <folly/ScopeGuard.h>
-#include <folly/experimental/symbolizer/Symbolizer.h>
+#include <folly/debugging/symbolizer/Symbolizer.h>
 #include <folly/lang/ToAscii.h>
 #include <folly/portability/SysSyscall.h>
 #include <folly/portability/Unistd.h>
@@ -138,6 +137,41 @@
     {0, nullptr, {}},
 };
 
+template <typename...>
+bool try_async_reraise(int signum, siginfo_t* info) {
+  using folly::detail::linux_syscall;
+#if defined(__linux__) && defined(SYS_pidfd_send_signal) && \
+    defined(SYS_pidfd_open)
+  constexpr long nr_pidfd_send_signal = SYS_pidfd_send_signal;
+  constexpr long nr_pidfd_open = SYS_pidfd_open;
+#else
+  constexpr long nr_pidfd_send_signal = -1;
+  constexpr long nr_pidfd_open = -1;
+#endif
+  if constexpr (kIsLinux && nr_pidfd_send_signal >= 0 && nr_pidfd_open >= 0) {
+    constexpr auto kPIdfdSelf = -10000;
+    // PIDFD_SELF handling introduced in linux-6.15 (released 2025-05-25)
+    if (0 == linux_syscall(nr_pidfd_send_signal, kPIdfdSelf, signum, info, 0)) {
+      return true;
+    }
+    // fallback using a real pidfd
+    // TODO: remove fallback once minimum is linux-6.15
+    if (errno != EBADF) { // EBADF here means PIDFD_SELF is not yet supported
+      return false;
+    }
+    auto const tid = linux_syscall(FOLLY_SYS_gettid);
+    // pidfd_open introduced in linux-5.3 (released 2019-09-15)
+    int const fd = to_narrow(linux_syscall(nr_pidfd_open, tid, 0));
+    if (-1 == fd) {
+      return false;
+    }
+    // pidfd_send_signal introduced in linux-5.1 (released 2019-05-05)
+    // no need to close(fd) after this - the process is about to terminate
+    return 0 == linux_syscall(nr_pidfd_send_signal, fd, signum, info, 0);
+  }
+  return false;
+}
+
 void signalHandler(int signum, siginfo_t* info, void* uctx);
 
 [[maybe_unused]] void callPreviousSignalHandler(int signum, siginfo_t* info) {
@@ -145,35 +179,38 @@
   // signal. The signal will remain blocked until the current call to the signal
   // handler returns.
   //
-  // For signals which arise from a faulting instruction, just restore the old
-  // disposition and return from the signal handler, returning control to the
-  // faulting instruction.
-  //
-  // Otherwise, restore the old disposition and explicitly re-raise the signal
-  // without returning to the faulting instruction.
-  //
-  // For these signals, we can approximately assume that when control returns to
-  // the faulting instruction, that instruction would fault again in the same
-  // way and generate the same signal again, triggering the default disposition
-  // for the signal. For example, an instruction which faulted, generating any
-  // of SIGSEGV, SIGILL, SIGFPE, or SIGBUS, can approximately be assumed to
-  // fault and generate the same signal again for the same reason.
-  //
-  // As a risk, it is possible that another thread or signal handler would
-  // resolve the fault concurrently with the current signal handling and before
-  // control returns to the faulting instruction, in which case the program
-  // continues without our signal handler registered for this signal anymore.
+  // On Linux, use pidfd_send_signal to re-raise the original signal with the
+  // original siginfo structure. Otherwise, just re-raise the original signal.
+  // Re-raising the original signal with the original siginfo enables the Linux
+  // kernel to record the true cause of the signal into the coredump. Otherwise,
+  // the kernel would see the explicit call to raise() as the cause and would
+  // record that in the coredump.
   //
-  // But, when it works, this technique preserves the true signal cause and
-  // makes that cause available to the debugger.
-  const bool fault = info->si_code > 0;
+  // For a signal arising from a faulting instruction, there is an alternative
+  // technique. After restoring disposition, the signal handler would simply
+  // return. Then the faulting instruction would execute again and be expected
+  // to trigger the same signal again. The second time, there would be no signal
+  // handler to handle the signal, so the kernel would see this second execution
+  // of the instruction as the true cause of the signal and would record that in
+  // the coredump. However, this technique is subject to the race where another
+  // thread might resolve the fault, causing the instruction's second execution
+  // to resume without fault. This can be a problem since we do want the process
+  // to terminate immediately with a coredump, and since the process resumes but
+  // without the corresponding signal handler anymore so the next time the same
+  // fault type happens there is no signal handler to report it.
   for (auto p = kFatalSignals; p->name; ++p) {
     if (p->number == signum) {
       sigaction(signum, &p->oldAction, nullptr);
-      if (!fault) {
-        raise(signum);
+      if (try_async_reraise(signum, info)) {
+        return;
       }
-      return;
+      // Unblock the signal before raising it. Since our handler doesn't use
+      // SA_NODEFER, the signal is currently blocked.
+      sigset_t mask;
+      sigemptyset(&mask);
+      sigaddset(&mask, signum);
+      pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
+      raise(signum);
     }
   }
 
@@ -182,6 +219,9 @@
   memset(&sa, 0, sizeof(sa));
   sa.sa_handler = SIG_DFL;
   sigaction(signum, &sa, nullptr);
+  if (try_async_reraise(signum, info)) {
+    return;
+  }
   raise(signum);
 }
 
@@ -516,7 +556,10 @@
 // Small sigaltstack size threshold.
 // 51392 is known to cause the signal handler to stack overflow during
 // symbolization of trivial async stacks (e.g [] { CHECK(false); co_return; }).
-constexpr size_t kSmallSigAltStackSize = 51392;
+// 54016 is known to cause the signal handler to stack overflow during
+// symbolization of less trivial async stacks. Setting 64KB to have a bit larger
+// and "less" magical threshold.
+constexpr size_t kSmallSigAltStackSize = 65536;
 
 [[maybe_unused]] bool isSmallSigAltStackEnabled() {
   stack_t ss;
diff --git a/folly/folly/debugging/symbolizer/SignalHandler.h b/folly/folly/debugging/symbolizer/SignalHandler.h
--- a/folly/folly/debugging/symbolizer/SignalHandler.h
+++ b/folly/folly/debugging/symbolizer/SignalHandler.h
@@ -47,7 +47,7 @@
  * All these fatal callback must be added before calling
  * installFatalSignalCallbacks(), below.
  */
-typedef void (*SignalCallback)();
+using SignalCallback = void (*)();
 void addFatalSignalCallback(SignalCallback cb);
 
 /**
diff --git a/folly/folly/debugging/symbolizer/StackTrace.cpp b/folly/folly/debugging/symbolizer/StackTrace.cpp
--- a/folly/folly/debugging/symbolizer/StackTrace.cpp
+++ b/folly/folly/debugging/symbolizer/StackTrace.cpp
@@ -18,11 +18,10 @@
 #include <folly/tracing/AsyncStack.h>
 
 #include <memory>
+#include <optional>
 
-#include <folly/CppAttributes.h>
 #include <folly/Portability.h>
 #include <folly/portability/Config.h>
-#include <folly/tracing/AsyncStack.h>
 
 #if FOLLY_HAVE_LIBUNWIND
 // Must be first to ensure that UNW_LOCAL_ONLY is defined
@@ -31,7 +30,7 @@
 #endif
 
 #if FOLLY_HAVE_BACKTRACE
-#include <execinfo.h>
+#include <execinfo.h> // @donotremove
 #endif
 
 namespace folly {
@@ -87,7 +86,36 @@
 
 #if FOLLY_HAVE_LIBUNWIND
 
-inline bool getFrameInfo(unw_cursor_t* cursor, uintptr_t& ip) {
+struct FrameInfo {
+  /// The instruction pointer (ip) of the frame.
+  uintptr_t uip;
+  bool isSignalFrame;
+
+  inline uintptr_t getAdjustedInstructionPointer(
+      std::optional<FrameInfo> frameInfoPrev) const {
+    bool adjustment;
+
+    if constexpr (folly::kIsArchAArch64) {
+      // This ip adjustment logic matches `unw_backtrace`.
+      // https://github.com/libunwind/libunwind/blob/v1.8.2/src/aarch64/Gtrace.c#L554-L555
+      // The ip is adjusted if there wasn't a previous frame,
+      // or if the previous frame was NOT a signal frame.
+      adjustment = !frameInfoPrev || !frameInfoPrev->isSignalFrame;
+    } else {
+      // Use previous instruction in normal (call) frames (because the
+      // return address might not be in the same function for noreturn
+      // functions) but not in signal frames.
+      adjustment = !this->isSignalFrame;
+    }
+
+    return this->uip - adjustment;
+  }
+};
+
+/// Gets the `FrameInfo` from libunwind and stores it in the out ref parameter.
+///
+/// @return true on success and false on error.
+inline bool getFrameInfo(unw_cursor_t* cursor, FrameInfo& frameInfo) {
   unw_word_t uip;
   if (unw_get_reg(cursor, UNW_REG_IP, &uip) < 0) {
     return false;
@@ -96,10 +124,10 @@
   if (r < 0) {
     return false;
   }
-  // Use previous instruction in normal (call) frames (because the
-  // return address might not be in the same function for noreturn functions)
-  // but not in signal frames.
-  ip = uip - (r == 0);
+  bool isSignalFrame = r > 0;
+
+  frameInfo = FrameInfo{uip, isSignalFrame};
+
   return true;
 }
 
@@ -122,9 +150,13 @@
   if (unw_init_local(&cursor, &context) < 0) {
     return -1;
   }
-  if (!getFrameInfo(&cursor, *addresses)) {
+  FrameInfo frameInfo;
+  if (!getFrameInfo(&cursor, frameInfo)) {
     return -1;
   }
+
+  std::optional<FrameInfo> frameInfoPrev; // no previous frame, yet.
+  *addresses = frameInfo.getAdjustedInstructionPointer(frameInfoPrev);
   ++addresses;
   size_t count = 1;
   for (; count != maxAddresses; ++count, ++addresses) {
@@ -135,9 +167,11 @@
     if (r == 0) {
       break;
     }
-    if (!getFrameInfo(&cursor, *addresses)) {
+    frameInfoPrev = frameInfo;
+    if (!getFrameInfo(&cursor, frameInfo)) {
       return -1;
     }
+    *addresses = frameInfo.getAdjustedInstructionPointer(frameInfoPrev);
   }
   return count;
 }
diff --git a/folly/folly/debugging/symbolizer/SymbolizePrinter.h b/folly/folly/debugging/symbolizer/SymbolizePrinter.h
--- a/folly/folly/debugging/symbolizer/SymbolizePrinter.h
+++ b/folly/folly/debugging/symbolizer/SymbolizePrinter.h
@@ -20,7 +20,7 @@
 
 #include <folly/FBString.h>
 #include <folly/Range.h>
-#include <folly/experimental/symbolizer/SymbolizedFrame.h>
+#include <folly/debugging/symbolizer/SymbolizedFrame.h>
 
 namespace folly {
 class IOBuf;
diff --git a/folly/folly/debugging/symbolizer/Symbolizer.cpp b/folly/folly/debugging/symbolizer/Symbolizer.cpp
--- a/folly/folly/debugging/symbolizer/Symbolizer.cpp
+++ b/folly/folly/debugging/symbolizer/Symbolizer.cpp
@@ -16,22 +16,17 @@
 
 #include <folly/debugging/symbolizer/Symbolizer.h>
 
-#include <climits>
-#include <cstdio>
 #include <cstdlib>
-#include <iostream>
 
 #include <folly/FileUtil.h>
 #include <folly/Memory.h>
 #include <folly/ScopeGuard.h>
-#include <folly/String.h>
 #include <folly/Synchronized.h>
 #include <folly/container/EvictingCacheMap.h>
+#include <folly/debugging/symbolizer/Dwarf.h>
+#include <folly/debugging/symbolizer/Elf.h>
+#include <folly/debugging/symbolizer/ElfCache.h>
 #include <folly/debugging/symbolizer/detail/Debug.h>
-#include <folly/experimental/symbolizer/Dwarf.h>
-#include <folly/experimental/symbolizer/Elf.h>
-#include <folly/experimental/symbolizer/ElfCache.h>
-#include <folly/experimental/symbolizer/LineReader.h>
 #include <folly/lang/SafeAssert.h>
 #include <folly/lang/ToAscii.h>
 #include <folly/memory/SanitizeAddress.h>
@@ -183,13 +178,12 @@
   frame.found = true;
   frame.addr = address;
   frame.file = file;
-  frame.name = file->getSymbolName(file->getDefinitionByAddress(address));
 #ifdef __roar__
-  if (!frame.name &&
-      setROARSymbolizedFrame(frame, address, mode, extraInlineFrames)) {
+  if (setROARSymbolizedFrame(frame, address, mode, extraInlineFrames)) {
     return;
   }
 #endif
+  frame.name = file->getSymbolName(file->getDefinitionByAddress(address));
 
   Dwarf(elfCache, file.get())
       .findAddress(address, mode, frame, extraInlineFrames);
@@ -203,6 +197,25 @@
 
 using UnsyncSymbolCache = EvictingCacheMap<uintptr_t, CachedSymbolizedFrames>;
 
+/**
+ * @param instructionAddr The address of an instruction after it has been
+ * adjusted by the linker's `l_addr`.
+ * @return true if the given address is contained in an executable segment of
+ * `elfFile`.
+ */
+bool containedInExecutableSegment(
+    const ElfFile& elfFile, ElfAddr instructionAddr) {
+  return elfFile.iterateProgramHeaders([&](const ElfPhdr& sh) {
+    bool executable = sh.p_flags & PF_X;
+    bool loadable = sh.p_type == PT_LOAD;
+    if (!(executable && loadable)) {
+      return false;
+    }
+    return sh.p_vaddr <= instructionAddr &&
+        instructionAddr < (sh.p_vaddr + sh.p_memsz);
+  });
+}
+
 } // namespace
 
 struct Symbolizer::SymbolCache : public Synchronized<UnsyncSymbolCache> {
@@ -334,9 +347,11 @@
 
       // Get the unrelocated, ELF-relative address by normalizing via the
       // address at which the object is loaded.
-      auto const adjusted = addr - reinterpret_cast<uintptr_t>(lmap->l_addr);
+      auto const eaddr = static_cast<ElfAddr>(addr);
+      auto const maddr = lmap->l_addr;
+      auto const adjusted = eaddr < maddr ? ~ElfAddr(0) : eaddr - maddr;
       size_t numInlined = 0;
-      if (elfFile->getSectionContainingAddress(adjusted)) {
+      if (containedInExecutableSegment(*elfFile, adjusted)) {
         if (mode_ == LocationInfoMode::FULL_WITH_INLINE &&
             frameCount > addrCount) {
           size_t maxInline = std::min<size_t>(
@@ -528,31 +543,54 @@
 constexpr size_t kMaxStackTraceDepth = 100;
 
 template <size_t N, typename StackTraceFunc>
-std::string getStackTraceStrImpl(StackTraceFunc func) {
+std::string getStackTraceStrImpl(
+    StackTraceFunc func, bool showFullInfo = false, size_t skip = 0) {
   FrameArray<N> addresses;
 
   if (!func(addresses)) {
     return "";
   } else {
-    ElfCache elfCache;
-    Symbolizer symbolizer(&elfCache);
-    symbolizer.symbolize(addresses);
-
-    StringSymbolizePrinter printer;
-    printer.println(addresses);
-    return printer.str();
+    return detail::getStackTraceStr(
+        addresses.addresses,
+        addresses.frames,
+        addresses.frameCount,
+        showFullInfo,
+        skip);
   }
 }
 } // namespace
 
-std::string getStackTraceStr() {
+namespace detail {
+std::string getStackTraceStr(
+    const uintptr_t* addresses,
+    SymbolizedFrame* frames,
+    size_t frameCount,
+    bool showFullInfo,
+    size_t skip) {
+  if (frameCount == 0 || skip >= frameCount) {
+    return {};
+  }
+
+  ElfCache elfCache;
+  LocationInfoMode mode =
+      showFullInfo ? LocationInfoMode::FULL : LocationInfoMode::FAST;
+  Symbolizer symbolizer(&elfCache, mode);
+  symbolizer.symbolize(addresses, frames, frameCount);
+
+  StringSymbolizePrinter printer;
+  printer.println(frames + skip, frameCount - skip);
+  return printer.str();
+}
+} // namespace detail
+
+std::string getStackTraceStr(bool showFullInfo, size_t skip) {
   return getStackTraceStrImpl<kMaxStackTraceDepth>(
-      getStackTrace<kMaxStackTraceDepth>);
+      getStackTrace<kMaxStackTraceDepth>, showFullInfo, skip);
 }
 
-std::string getAsyncStackTraceStr() {
+std::string getAsyncStackTraceStr(size_t skip) {
   return getStackTraceStrImpl<kMaxStackTraceDepth>(
-      getAsyncStackTraceSafe<kMaxStackTraceDepth>);
+      getAsyncStackTraceSafe<kMaxStackTraceDepth>, skip);
 }
 
 std::vector<std::string> getSuspendedStackTraces() {
diff --git a/folly/folly/debugging/symbolizer/Symbolizer.h b/folly/folly/debugging/symbolizer/Symbolizer.h
--- a/folly/folly/debugging/symbolizer/Symbolizer.h
+++ b/folly/folly/debugging/symbolizer/Symbolizer.h
@@ -25,11 +25,11 @@
 #include <folly/Optional.h>
 #include <folly/Range.h>
 #include <folly/String.h>
-#include <folly/experimental/symbolizer/Dwarf.h>
-#include <folly/experimental/symbolizer/ElfCache.h>
-#include <folly/experimental/symbolizer/StackTrace.h>
-#include <folly/experimental/symbolizer/SymbolizePrinter.h>
-#include <folly/experimental/symbolizer/SymbolizedFrame.h>
+#include <folly/debugging/symbolizer/Dwarf.h>
+#include <folly/debugging/symbolizer/ElfCache.h>
+#include <folly/debugging/symbolizer/StackTrace.h>
+#include <folly/debugging/symbolizer/SymbolizePrinter.h>
+#include <folly/debugging/symbolizer/SymbolizedFrame.h>
 #include <folly/io/IOBuf.h>
 #include <folly/portability/Config.h>
 #include <folly/portability/Unistd.h>
@@ -56,6 +56,13 @@
     return false;
   }
 }
+
+std::string getStackTraceStr(
+    const uintptr_t* addresses,
+    SymbolizedFrame* frames,
+    size_t frameCount,
+    bool showFullInfo,
+    size_t skip = 0);
 } // namespace detail
 
 // Always inline these functions; they don't do much, and unittests rely
@@ -269,22 +276,27 @@
 #if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF
 
 /**
- * Gets the stack trace for the current thread and returns a string
- * representation. Convenience function meant for debugging and logging.
- * Empty string indicates stack trace functionality is not available.
+ * Gets the stack trace for the current thread, skipping the top `skip` frames,
+ * and returns a string representation. Convenience function meant for
+ * debugging and logging. Empty string indicates stack trace functionality
+ * is not available.
  *
  * NOT async-signal-safe.
+ * @param showFullInfo If true, include file names and line numbers (when
+ * available).
+ * @param skip Skip the top `skip` frames.
  */
-std::string getStackTraceStr();
+std::string getStackTraceStr(bool showFullInfo = false, size_t skip = 0);
 
 /**
- * Gets the async stack trace for the current thread and returns a string
- * representation. Convenience function meant for debugging and logging.
- * Empty string indicates stack trace functionality is not available.
+ * Gets the async stack trace for the current thread, skipping the top `skip`
+ * frames, and returns a string representation. Convenience function meant for
+ * debugging and logging. Empty string indicates stack trace functionality
+ * is not available.
  *
  * NOT async-signal-safe.
  */
-std::string getAsyncStackTraceStr();
+std::string getAsyncStackTraceStr(size_t skip = 0);
 
 /**
  * Get the async stack traces (string representation) for suspended
@@ -299,18 +311,45 @@
 // Define these in the header, as headers are always available, but not all
 // platforms can link against the symbolizer library cpp sources.
 
-inline std::string getStackTraceStr() {
+inline std::string getStackTraceStr(
+    bool /*showFullInfo*/ = false, size_t /*skip*/ = 0) {
   return "";
 }
 
-inline std::string getAsyncStackTraceStr() {
+inline std::string getAsyncStackTraceStr(size_t /*skip*/ = 0) {
   return "";
 }
 
 inline std::vector<std::string> getSuspendedStackTraces() {
   return {};
 }
+
+namespace detail {
+inline std::string getStackTraceStr(
+    const uintptr_t* /*addresses*/,
+    SymbolizedFrame* /*frames*/,
+    size_t /*frameCount*/,
+    bool /*showFullInfo*/,
+    size_t /*skip*/) {
+  return "";
+}
+} // namespace detail
 #endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF
+
+/**
+ * Convert FrameArray to string representation.
+ * Convenience function for debugging and logging.
+ */
+template <size_t N>
+FOLLY_ALWAYS_INLINE std::string getStackTraceStr(
+    FrameArray<N>& fa, bool showFullInfo = false, size_t skip = 0);
+
+template <size_t N>
+inline std::string getStackTraceStr(
+    FrameArray<N>& fa, bool showFullInfo, size_t skip) {
+  return detail::getStackTraceStr(
+      fa.addresses, fa.frames, fa.frameCount, showFullInfo, skip);
+}
 
 #if FOLLY_HAVE_SWAPCONTEXT
 
diff --git a/folly/folly/debugging/symbolizer/detail/Debug.cpp b/folly/folly/debugging/symbolizer/detail/Debug.cpp
--- a/folly/folly/debugging/symbolizer/detail/Debug.cpp
+++ b/folly/folly/debugging/symbolizer/detail/Debug.cpp
@@ -22,10 +22,6 @@
 #include <dlfcn.h>
 #endif
 
-#if FOLLY_HAVE_ELF
-#include <link.h>
-#endif
-
 #if defined(__APPLE__) && !TARGET_OS_OSX
 #define FOLLY_DETAIL_HAS_R_DEBUG 0
 #elif !defined(__linux__) || !FOLLY_HAVE_ELF || !FOLLY_HAVE_DWARF
diff --git a/folly/folly/detail/AsyncTrace.cpp b/folly/folly/detail/AsyncTrace.cpp
--- a/folly/folly/detail/AsyncTrace.cpp
+++ b/folly/folly/detail/AsyncTrace.cpp
@@ -16,8 +16,6 @@
 
 #include <folly/detail/AsyncTrace.h>
 
-#include <folly/Portability.h>
-
 namespace folly {
 namespace async_tracing {
 FOLLY_ATTR_WEAK void logSetGlobalCPUExecutor(Executor*) noexcept {}
diff --git a/folly/folly/detail/AtomicUnorderedMapUtils.h b/folly/folly/detail/AtomicUnorderedMapUtils.h
--- a/folly/folly/detail/AtomicUnorderedMapUtils.h
+++ b/folly/folly/detail/AtomicUnorderedMapUtils.h
@@ -16,65 +16,37 @@
 
 #pragma once
 
-#include <atomic>
-#include <cassert>
 #include <cstdint>
-#include <system_error>
+#include <cstdlib>
 
-#include <folly/Exception.h>
-#include <folly/portability/SysMman.h>
-#include <folly/portability/Unistd.h>
+#include <folly/lang/Exception.h>
+#include <folly/memory/Malloc.h>
+
 namespace folly {
 namespace detail {
 
-class MMapAlloc {
- private:
-  size_t computeSize(size_t size) {
-    long pagesize = sysconf(_SC_PAGESIZE);
-    size_t mmapLength = ((size - 1) & ~(pagesize - 1)) + pagesize;
-    assert(size <= mmapLength && mmapLength < size + pagesize);
-    assert((mmapLength % pagesize) == 0);
-    return mmapLength;
-  }
-
+class MallocAlloc {
  public:
   void* allocate(size_t size) {
-    auto len = computeSize(size);
-
-    int extraflags = 0;
-#if defined(MAP_POPULATE)
-    extraflags |= MAP_POPULATE;
-#endif
-    // MAP_HUGETLB is a perf win, but requires cooperation from the
-    // deployment environment (and a change to computeSize()).
-    void* mem = static_cast<void*>(mmap(
-        nullptr,
-        len,
-        PROT_READ | PROT_WRITE,
-        MAP_PRIVATE | MAP_ANONYMOUS | extraflags,
-        -1,
-        0));
-    if (mem == reinterpret_cast<void*>(-1)) {
-      throw std::system_error(errno, errorCategoryForErrnoDomain());
+    void* p = std::malloc(size);
+    if (p == nullptr) {
+      throw_exception<std::bad_alloc>();
     }
-#if !defined(MAP_POPULATE) && defined(MADV_WILLNEED)
-    madvise(mem, size, MADV_WILLNEED);
-#endif
-
-    return mem;
+    return p;
   }
 
-  void deallocate(void* p, size_t size) {
-    auto len = computeSize(size);
-    munmap(p, len);
-  }
+  void deallocate(void* p, size_t size) { sizedFree(p, size); }
 };
 
 template <typename Allocator>
 struct GivesZeroFilledMemory : public std::false_type {};
 
-template <>
-struct GivesZeroFilledMemory<MMapAlloc> : public std::true_type {};
+/*
+ * Example of how to specialize GivesZeroFilledMemory for custom allocator.
+ *
+ * template <>
+ * struct GivesZeroFilledMemory<Alloc> : public std::true_type {};
+ */
 
 } // namespace detail
 } // namespace folly
diff --git a/folly/folly/detail/FileUtilDetail.h b/folly/folly/detail/FileUtilDetail.h
--- a/folly/folly/detail/FileUtilDetail.h
+++ b/folly/folly/detail/FileUtilDetail.h
@@ -18,6 +18,7 @@
 
 #include <cerrno>
 #include <cstddef>
+#include <functional>
 #include <string>
 #include <type_traits>
 
@@ -34,9 +35,9 @@
 //  against interrupt and partial op completions.
 
 // Wrap call to f(args) in loop to retry on EINTR
-template <class F, class... Args>
-ssize_t wrapNoInt(F f, Args... args) {
-  ssize_t r;
+template <class F, class... Args, class R = std::invoke_result_t<F&, Args&...>>
+R wrapNoInt(F f, Args... args) {
+  R r;
   do {
     r = f(args...);
   } while (r == -1 && errno == EINTR);
diff --git a/folly/folly/detail/GroupVarintDetail.h b/folly/folly/detail/GroupVarintDetail.h
--- a/folly/folly/detail/GroupVarintDetail.h
+++ b/folly/folly/detail/GroupVarintDetail.h
@@ -48,11 +48,11 @@
 template <typename T>
 class GroupVarintBase {
  protected:
-  typedef GroupVarintTraits<T> Traits;
+  using Traits = GroupVarintTraits<T>;
   enum : uint32_t { kHeaderSize = Traits::kHeaderSize };
 
  public:
-  typedef T type;
+  using type = T;
 
   /**
    * Number of integers encoded / decoded in one pass.
@@ -93,7 +93,7 @@
   }
 
  private:
-  typedef GroupVarint<T> Derived;
+  using Derived = GroupVarint<T>;
   enum { kFullGroupSize = kHeaderSize + kGroupSize * sizeof(type) };
 };
 
diff --git a/folly/folly/detail/IPAddress.cpp b/folly/folly/detail/IPAddress.cpp
--- a/folly/folly/detail/IPAddress.cpp
+++ b/folly/folly/detail/IPAddress.cpp
@@ -28,10 +28,11 @@
 }
 
 [[noreturn]] void getNthMSBitImplThrow(size_t bitCount, sa_family_t family) {
-  throw std::invalid_argument(fmt::format(
-      FOLLY_FMT_COMPILE("Bit index must be < {} for addresses of type: {}"),
-      bitCount,
-      familyNameStr(family)));
+  throw std::invalid_argument(
+      fmt::format(
+          FOLLY_FMT_COMPILE("Bit index must be < {} for addresses of type: {}"),
+          bitCount,
+          familyNameStr(family)));
 }
 } // namespace detail
 } // namespace folly
diff --git a/folly/folly/detail/IPAddressSource.h b/folly/folly/detail/IPAddressSource.h
--- a/folly/folly/detail/IPAddressSource.h
+++ b/folly/folly/detail/IPAddressSource.h
@@ -75,10 +75,11 @@
         0xff // /8
     }};
     if (oneMask > kBitCount || twoMask > kBitCount) {
-      throw std::invalid_argument(fmt::format(
-          "Invalid mask length: {}. Mask length must be <= {}",
-          std::max(oneMask, twoMask),
-          kBitCount));
+      throw std::invalid_argument(
+          fmt::format(
+              "Invalid mask length: {}. Mask length must be <= {}",
+              std::max(oneMask, twoMask),
+              kBitCount));
     }
 
     auto mask = std::min(oneMask, twoMask);
diff --git a/folly/folly/detail/MPMCPipelineDetail.h b/folly/folly/detail/MPMCPipelineDetail.h
--- a/folly/folly/detail/MPMCPipelineDetail.h
+++ b/folly/folly/detail/MPMCPipelineDetail.h
@@ -26,7 +26,7 @@
 template <class T, size_t Amp>
 class MPMCPipelineStage {
  public:
-  typedef T value_type;
+  using value_type = T;
   static constexpr size_t kAmplification = Amp;
 };
 
@@ -39,13 +39,13 @@
 template <class T>
 struct PipelineStageInfo {
   static constexpr size_t kAmplification = 1;
-  typedef T value_type;
+  using value_type = T;
 };
 
 template <class T, size_t Amp>
 struct PipelineStageInfo<MPMCPipelineStage<T, Amp>> {
   static constexpr size_t kAmplification = Amp;
-  typedef T value_type;
+  using value_type = T;
 };
 
 /**
@@ -54,7 +54,7 @@
 template <class T>
 class MPMCPipelineStageImpl {
  public:
-  typedef T value_type;
+  using value_type = T;
   template <class U, class... Stages>
   friend class MPMCPipeline;
 
diff --git a/folly/folly/detail/MemoryIdler.cpp b/folly/folly/detail/MemoryIdler.cpp
--- a/folly/folly/detail/MemoryIdler.cpp
+++ b/folly/folly/detail/MemoryIdler.cpp
@@ -17,9 +17,7 @@
 #include <folly/detail/MemoryIdler.h>
 
 #include <climits>
-#include <cstdio>
 #include <cstring>
-#include <utility>
 
 #include <folly/GLog.h>
 #include <folly/Portability.h>
@@ -55,7 +53,7 @@
   // stack on demand. Before the main thread stack grows to its full extent, the
   // vma corresponding to the main thread stack is not yet fully allocated. It's
   // possible for the kernel to allocate the not-yet-allocated main thread stack
-  // vma to ramdon sbrk() or mmap() requests, and for the resulting regions from
+  // vma to random sbrk() or mmap() requests, and for the resulting regions from
   // these requests to be used by other user code. If this happens, the madvise-
   // dontneed here is dangerous - it can zero arbitrary heap buffers! So it must
   // be skipped. In the case where this runs a fork() child in that thread which
diff --git a/folly/folly/detail/MemoryIdler.h b/folly/folly/detail/MemoryIdler.h
--- a/folly/folly/detail/MemoryIdler.h
+++ b/folly/folly/detail/MemoryIdler.h
@@ -79,9 +79,10 @@
 
     // hash the pthread_t and the time to get the adjustment
     // Standard hash func isn't very good, so bit mix the result
-    uint64_t h = folly::hash::twang_mix64(folly::hash::hash_combine(
-        getCurrentThreadID(),
-        std::chrono::system_clock::now().time_since_epoch().count()));
+    uint64_t h = folly::hash::twang_mix64(
+        folly::hash::hash_combine(
+            getCurrentThreadID(),
+            std::chrono::system_clock::now().time_since_epoch().count()));
 
     // multiplying the duration by a floating point doesn't work, grr
     auto extraFrac = timeoutVariationFrac /
diff --git a/folly/folly/detail/PerfScoped.cpp b/folly/folly/detail/PerfScoped.cpp
--- a/folly/folly/detail/PerfScoped.cpp
+++ b/folly/folly/detail/PerfScoped.cpp
@@ -24,9 +24,12 @@
 #include <folly/testing/TestUtil.h>
 #endif
 
+#include <filesystem>
 #include <stdexcept>
 #include <thread>
 
+#include <boost/regex.hpp>
+
 namespace folly {
 namespace detail {
 
@@ -41,10 +44,10 @@
   std::vector<std::string> res{std::string(kPerfBinaryPath)};
   res.insert(res.end(), passed.begin(), passed.end());
 
-  res.push_back("-p");
+  res.emplace_back("-p");
   res.push_back(folly::to<std::string>(get_cached_pid()));
   if (output) {
-    res.push_back("--output");
+    res.emplace_back("--output");
     res.push_back(output->path().string());
   }
   return res;
@@ -72,6 +75,8 @@
   PerfScopedImpl& operator=(PerfScopedImpl&&) = delete;
 
   ~PerfScopedImpl() noexcept {
+    waitUntilAttached();
+
     proc_.sendSignal(SIGINT);
     proc_.wait();
 
@@ -81,6 +86,22 @@
   }
 
  private:
+  void waitUntilAttached() {
+    const boost::regex regex{R"(anon_inode:\[perf_event(:\w+)?\])"};
+    const auto slashproc = std::filesystem::path("/proc");
+    const auto fddir = slashproc / folly::to<std::string>(proc_.pid()) / "fd";
+    while (true) {
+      for (const auto& entry : std::filesystem::directory_iterator(fddir)) {
+        std::error_code ec;
+        const auto target = std::filesystem::read_symlink(entry.path(), ec);
+        if (boost::regex_match(target.string(), regex)) {
+          return;
+        }
+      }
+      std::this_thread::yield();
+    }
+  }
+
   test::TemporaryFile outputFile_;
   Subprocess proc_;
   std::string* output_;
diff --git a/folly/folly/detail/PolyDetail.h b/folly/folly/detail/PolyDetail.h
--- a/folly/folly/detail/PolyDetail.h
+++ b/folly/folly/detail/PolyDetail.h
@@ -619,8 +619,9 @@
 }
 
 inline void* noopExec(Op op, Data*, void*) {
-  if (op == Op::eAddr)
+  if (op == Op::eAddr) {
     throw_exception<BadPolyAccess>();
+  }
   return const_cast<void*>(static_cast<void const*>(&typeid(void)));
 }
 
@@ -803,11 +804,12 @@
 template <class Fun> // Fun = R(As...) const
 struct Sig {
   template <class T>
-  constexpr Fun T::*operator()(Fun T::*t) const /* nolint */ volatile noexcept {
+  constexpr Fun T::* operator()(Fun T::* t) const
+      /* nolint */ volatile noexcept {
     return t;
   }
   template <class F, class T>
-  constexpr F T::*operator()(F T::*t) const /* nolint */ volatile noexcept {
+  constexpr F T::* operator()(F T::* t) const /* nolint */ volatile noexcept {
     return t;
   }
 };
@@ -820,7 +822,7 @@
   using Sig<R() const>::operator();
 
   template <class T>
-  constexpr Fun T::*operator()(Fun T::*t) const noexcept {
+  constexpr Fun T::* operator()(Fun T::* t) const noexcept {
     return t;
   }
 };
@@ -831,7 +833,7 @@
   using Sig<R(As...) const>::operator();
 
   template <class T>
-  constexpr Fun T::*operator()(Fun T::*t) const noexcept {
+  constexpr Fun T::* operator()(Fun T::* t) const noexcept {
     return t;
   }
   constexpr Fun* operator()(Fun* t) const noexcept { return t; }
diff --git a/folly/folly/detail/SplitStringSimd.cpp b/folly/folly/detail/SplitStringSimd.cpp
deleted file mode 100644
--- a/folly/folly/detail/SplitStringSimd.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/detail/SplitStringSimd.h>
-#include <folly/detail/SplitStringSimdImpl.h>
-
-#include <folly/FBString.h>
-#include <folly/FBVector.h>
-#include <folly/small_vector.h>
-
-namespace folly {
-namespace detail {
-
-template <typename Container>
-void SimdSplitByCharImpl<Container>::keepEmpty(
-    char sep, folly::StringPiece what, Container& res) {
-  PlatformSimdSplitByChar<
-      simd::detail::SimdPlatform<std::uint8_t>,
-      /*ignoreEmpty*/ false>{}(sep, what, res);
-}
-
-template <typename Container>
-void SimdSplitByCharImpl<Container>::dropEmpty(
-    char sep, folly::StringPiece what, Container& res) {
-  PlatformSimdSplitByChar<
-      simd::detail::SimdPlatform<std::uint8_t>,
-      /*ignoreEmpty*/ true>{}(sep, what, res);
-}
-
-template <typename Container>
-void SimdSplitByCharImplToStrings<Container>::keepEmpty(
-    char sep, folly::StringPiece what, Container& res) {
-  PlatformSimdSplitByChar<
-      simd::detail::SimdPlatform<std::uint8_t>,
-      /*ignoreEmpty*/ false>{}(sep, what, res);
-}
-
-template <typename Container>
-void SimdSplitByCharImplToStrings<Container>::dropEmpty(
-    char sep, folly::StringPiece what, Container& res) {
-  PlatformSimdSplitByChar<
-      simd::detail::SimdPlatform<std::uint8_t>,
-      /*ignoreEmpty*/ true>{}(sep, what, res);
-}
-
-// clang-format off
-#define FOLLY_DETAIL_DEFINE_ALL_SIMD_SPLIT_OVERLOADS(...) \
-  template struct SimdSplitByCharImpl<std::vector<__VA_ARGS__>>; \
-  template struct SimdSplitByCharImpl<folly::fbvector<__VA_ARGS__, std::allocator<__VA_ARGS__>>>; \
-  template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 1, void>>; \
-  template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 2, void>>; \
-  template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 3, void>>; \
-  template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 4, void>>; \
-  template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 5, void>>; \
-  template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 6, void>>; \
-  template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 7, void>>; \
-  template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 8, void>>;
-// clang-format on
-
-FOLLY_DETAIL_DEFINE_ALL_SIMD_SPLIT_OVERLOADS(folly::StringPiece)
-
-FOLLY_DETAIL_DEFINE_ALL_SIMD_SPLIT_OVERLOADS(std::string_view)
-
-#undef FOLLY_DETAIL_DEFINE_ALL_SIMD_SPLIT_OVERLOADS
-
-template struct SimdSplitByCharImplToStrings<std::vector<std::string>>;
-template struct SimdSplitByCharImplToStrings<std::vector<fbstring>>;
-template struct SimdSplitByCharImplToStrings<fbvector<std::string>>;
-template struct SimdSplitByCharImplToStrings<fbvector<fbstring>>;
-} // namespace detail
-} // namespace folly
diff --git a/folly/folly/detail/SplitStringSimd.h b/folly/folly/detail/SplitStringSimd.h
--- a/folly/folly/detail/SplitStringSimd.h
+++ b/folly/folly/detail/SplitStringSimd.h
@@ -16,136 +16,26 @@
 
 #pragma once
 
-#include <string>
-#include <vector>
 #include <folly/Range.h>
+#include <folly/detail/SplitStringSimdImpl.h>
 
 namespace folly {
-
-template <typename T, std::size_t M, typename P>
-class small_vector;
-
-template <typename T, typename Allocator>
-class fbvector;
-
-template <class Char>
-class fbstring_core;
-
-template <typename E, typename T, typename A, typename Storage>
-class basic_fbstring;
-
 namespace detail {
 
-using PredeclareFbString = basic_fbstring<
-    char,
-    std::char_traits<char>,
-    std::allocator<char>,
-    fbstring_core<char>>;
-
 template <typename Container>
-struct SimdSplitByCharImpl {
-  static void keepEmpty(char sep, folly::StringPiece what, Container& res);
-  static void dropEmpty(char sep, folly::StringPiece what, Container& res);
-};
-
-// Different name to easier identify in the stack potential performance issues
-template <typename Container>
-struct SimdSplitByCharImplToStrings {
-  static void keepEmpty(char sep, folly::StringPiece what, Container& res);
-  static void dropEmpty(char sep, folly::StringPiece what, Container& res);
-};
-
-template <typename T>
-constexpr bool isSimdSplitSupportedStringViewType =
-    std::is_same<T, folly::StringPiece>::value ||
-    std::is_same<T, std::string_view>::value;
-
-template <typename T>
-constexpr bool isSimdSplitSupportedStringType =
-    std::is_same<T, PredeclareFbString>::value ||
-    std::is_same<T, std::string>::value;
-
-template <typename>
-struct SimdSplitByCharIsDefinedFor {
-  static constexpr bool value = false;
-};
-
-template <typename T>
-struct SimdSplitByCharIsDefinedFor<std::vector<T>> {
-  static constexpr bool value = isSimdSplitSupportedStringViewType<T> ||
-      isSimdSplitSupportedStringType<T>;
-};
-
-template <typename T, typename A>
-struct SimdSplitByCharIsDefinedFor<folly::fbvector<T, A>>
-    : SimdSplitByCharIsDefinedFor<std::vector<T, A>> {};
-
-template <typename T, std::size_t M>
-struct SimdSplitByCharIsDefinedFor<folly::small_vector<T, M, void>> {
-  static constexpr bool value =
-      isSimdSplitSupportedStringViewType<T> && 0 < M && M <= 8;
-};
-
-template <typename Container>
-std::enable_if_t<
-    isSimdSplitSupportedStringViewType<typename Container::value_type>>
-simdSplitByChar(
-    char sep, folly::StringPiece what, Container& res, bool ignoreEmpty) {
-  static_assert(
-      SimdSplitByCharIsDefinedFor<Container>::value,
-      "simd split by char is supported only for vector/fbvector/small_vector, with small size <= 8."
-      " The resulting string type has to string_view or StringPiece."
-      " There is also a special case of (fb)vector<(fb)string> for legacy compatibility");
-  if (ignoreEmpty) {
-    SimdSplitByCharImpl<Container>::dropEmpty(sep, what, res);
-  } else {
-    SimdSplitByCharImpl<Container>::keepEmpty(sep, what, res);
-  }
-}
-
-template <typename Container>
-std::enable_if_t<isSimdSplitSupportedStringType<typename Container::value_type>>
-simdSplitByChar(
+void simdSplitByChar(
     char sep, folly::StringPiece what, Container& res, bool ignoreEmpty) {
-  static_assert(
-      SimdSplitByCharIsDefinedFor<Container>::value,
-      "simd split by char is supported only for vector/fbvector/small_vector, with small size <= 8."
-      " The resulting string type has to string_view or StringPiece."
-      " There is also a special case of (fb)vector<(fb)string> for legacy compatibility");
+  using Platform = simd::detail::SimdPlatform<std::uint8_t>;
   if (ignoreEmpty) {
-    SimdSplitByCharImplToStrings<Container>::dropEmpty(sep, what, res);
+    PlatformSimdSplitByChar<
+        Platform,
+        /*ignoreEmpty*/ true>{}(sep, what, res);
   } else {
-    SimdSplitByCharImplToStrings<Container>::keepEmpty(sep, what, res);
+    PlatformSimdSplitByChar<
+        Platform,
+        /*ignoreEmpty*/ false>{}(sep, what, res);
   }
 }
-
-// clang-format off
-#define FOLLY_DETAIL_DECLARE_ALL_SIMD_SPLIT_OVERLOADS(...) \
-  extern template struct SimdSplitByCharImpl<std::vector<__VA_ARGS__>>; \
-  extern template struct SimdSplitByCharImpl<folly::fbvector<__VA_ARGS__, std::allocator<__VA_ARGS__>>>; \
-  extern template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 1, void>>; \
-  extern template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 2, void>>; \
-  extern template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 3, void>>; \
-  extern template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 4, void>>; \
-  extern template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 5, void>>; \
-  extern template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 6, void>>; \
-  extern template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 7, void>>; \
-  extern template struct SimdSplitByCharImpl<folly::small_vector<__VA_ARGS__, 8, void>>;
-// clang-format on
-
-FOLLY_DETAIL_DECLARE_ALL_SIMD_SPLIT_OVERLOADS(folly::StringPiece)
-
-FOLLY_DETAIL_DECLARE_ALL_SIMD_SPLIT_OVERLOADS(std::string_view)
-
-extern template struct SimdSplitByCharImplToStrings<std::vector<std::string>>;
-extern template struct SimdSplitByCharImplToStrings<
-    std::vector<PredeclareFbString>>;
-extern template struct SimdSplitByCharImplToStrings<
-    fbvector<std::string, std::allocator<std::string>>>;
-extern template struct SimdSplitByCharImplToStrings<
-    fbvector<PredeclareFbString, std::allocator<PredeclareFbString>>>;
-
-#undef FOLLY_DETAIL_DECLARE_ALL_SIMD_SPLIT_OVERLOADS
 
 } // namespace detail
 } // namespace folly
diff --git a/folly/folly/detail/SplitStringSimdImpl.h b/folly/folly/detail/SplitStringSimdImpl.h
--- a/folly/folly/detail/SplitStringSimdImpl.h
+++ b/folly/folly/detail/SplitStringSimdImpl.h
@@ -32,17 +32,12 @@
 #include <arm_neon.h>
 #endif
 
-// This file is not supposed to be included by users.
-// It should be included in CPP file which exposes apis.
-// It is a header file to test different platforms.
-
-// All funcitons are force inline because they are merged into big hiddend
-// noinline functions
 namespace folly {
 namespace detail {
 
 template <bool ignoreEmpty, typename Container>
-void splitByCharScalar(char sep, folly::StringPiece what, Container& res) {
+FOLLY_NOINLINE void splitByCharScalar(
+    char sep, folly::StringPiece what, Container& res) {
   const char* prev = what.data();
   const char* f = prev;
   const char* l = what.data() + what.size();
@@ -72,8 +67,8 @@
   using reg_t = typename Platform::reg_t;
 
   template <typename Container>
-  FOLLY_ALWAYS_INLINE void emplaceBack(
-      Container& res, const std::uint8_t* f, const std::uint8_t* l) const {
+  FOLLY_ALWAYS_INLINE static void emplaceBack(
+      Container& res, const std::uint8_t* f, const std::uint8_t* l) {
     if (ignoreEmpty && f == l) {
       return;
     }
@@ -81,12 +76,11 @@
   }
 
   template <typename Uint, typename BitsPerElement, typename Container>
-  FOLLY_ALWAYS_INLINE void outputStringsFoMmask(
+  FOLLY_ALWAYS_INLINE static void outputStringsFoMmask(
       std::pair<Uint, BitsPerElement> mmask,
       const std::uint8_t* pos,
       const std::uint8_t*& prev,
-      Container& res) const { // reserve was not beneficial on benchmarks.
-
+      Container& res) {
     Uint mmaskBits = mmask.first;
     while (mmaskBits) {
       auto counted = folly::findFirstSet(mmaskBits) - 1;
@@ -103,7 +97,6 @@
 
   template <typename Container>
   struct ForEachDelegate {
-    const PlatformSimdSplitByChar& self;
     std::uint8_t sep;
     const std::uint8_t*& prev;
     Container& res;
@@ -114,14 +107,14 @@
       reg_t loaded = Platform::loada(ptr, ignore);
       auto mmask =
           simd::movemask<std::uint8_t>(Platform::equal(loaded, sep), ignore);
-      self.outputStringsFoMmask(mmask, ptr, prev, res);
+      outputStringsFoMmask(mmask, ptr, prev, res);
       return false;
     }
   };
 
   template <typename Container>
-  FOLLY_ALWAYS_INLINE void operator()(
-      char sep, folly::StringPiece what, Container& res) const {
+  FOLLY_NOINLINE static void simdSplitByChar(
+      char sep, folly::StringPiece what, Container& res) {
     const std::uint8_t* what_f =
         reinterpret_cast<const std::uint8_t*>(what.data());
     const std::uint8_t* what_l = what_f + what.size();
@@ -129,10 +122,16 @@
     const std::uint8_t* prev = what_f;
 
     ForEachDelegate<Container> delegate{
-        *this, static_cast<std::uint8_t>(sep), prev, res};
+        static_cast<std::uint8_t>(sep), prev, res};
     simd::detail::simdForEachAligning</*unrolling*/ 1>(
         Platform::kCardinal, what_f, what_l, delegate);
     emplaceBack(res, prev, what_l);
+  }
+
+  template <typename Container>
+  FOLLY_ALWAYS_INLINE void operator()(
+      char sep, folly::StringPiece what, Container& res) const {
+    return simdSplitByChar(sep, what, res);
   }
 };
 
diff --git a/folly/folly/detail/ThreadLocalDetail.cpp b/folly/folly/detail/ThreadLocalDetail.cpp
--- a/folly/folly/detail/ThreadLocalDetail.cpp
+++ b/folly/folly/detail/ThreadLocalDetail.cpp
@@ -23,8 +23,10 @@
 #include <folly/ConstexprMath.h>
 #include <folly/Utility.h>
 #include <folly/detail/thread_local_globals.h>
+#include <folly/hash/MurmurHash.h>
 #include <folly/lang/Hint.h>
 #include <folly/memory/SanitizeLeak.h>
+#include <folly/random/hash.h>
 #include <folly/synchronization/CallOnce.h>
 
 constexpr auto kSmallGrowthFactor = 1.1;
@@ -33,13 +35,17 @@
 namespace folly {
 namespace threadlocal_detail {
 
-struct rand_engine {
-  using result_type = unsigned int;
-  result_type operator()() { return to_unsigned(std::rand()); }
-  static constexpr result_type min() { return 0; }
-  static constexpr result_type max() { return RAND_MAX; }
+namespace {
+
+struct murmur_hash_fn {
+  std::uint64_t operator()(std::uint64_t const val) const noexcept {
+    auto const ptr = reinterpret_cast<const char*>(&val);
+    return folly::hash::murmurHash64(ptr, sizeof(val), 0u);
+  }
 };
 
+} // namespace
+
 SharedPtrDeleter::SharedPtrDeleter(std::shared_ptr<void> const& ts) noexcept
     : ts_{ts} {}
 SharedPtrDeleter::SharedPtrDeleter(SharedPtrDeleter const& that) noexcept
@@ -67,7 +73,10 @@
     return false;
   }
   auto const size = threadElements.size();
-  rand_engine rng;
+  // NOLINTNEXTLINE
+  static std::atomic<std::uint64_t> rng_seed_{to_unsigned(std::rand())};
+  using engine = hash_counter_engine<murmur_hash_fn, std::uint64_t>;
+  thread_local engine rng{rng_seed_.fetch_add(1, std::memory_order_relaxed)};
   std::uniform_int_distribution<size_t> dist{0, size - 1};
   if (!(dist(rng) < constexpr_log2(size))) {
     return true;
@@ -182,7 +191,8 @@
     return true;
   }
 
-  auto rlock = meta->allId2ThreadEntrySets_[id].tryRLock();
+  auto& set = meta->allId2ThreadEntrySets_[id];
+  auto rlock = set.tryRLock();
   if (!rlock) {
     // Try lock failed. Skip checking in this case. Avoids
     // getting stuck in case this validation is called when
@@ -190,6 +200,14 @@
     return true;
   }
 
+  // Check if there are pending inserts. If so, skip validation as the
+  // ThreadEntrySet may not have been updated yet due to the optimization
+  // in resetElementImplAfterSet that defers updates when the lock cannot
+  // be acquired.
+  if (set.hasPendingInserts()) {
+    return true;
+  }
+
   return elements[id].ptr == rlock->getPtrForThread(this);
 }
 
@@ -260,8 +278,12 @@
   static PthreadKeyUnregister instance_;
 };
 
-StaticMetaBase::StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict)
-    : nextId_(1), threadEntry_(threadEntry), strict_(strict) {
+StaticMetaBase::StaticMetaBase(
+    ThreadEntry* (*threadEntry)(), bool strict, bool allowsAccessAllThreads)
+    : nextId_(1),
+      threadEntry_(threadEntry),
+      strict_(strict),
+      allowsAccessAllThreads_(allowsAccessAllThreads) {
   int ret = pthread_key_create(&pthreadKey_, &onThreadExit);
   checkPosixError(ret, "pthread_key_create failed");
   PthreadKeyUnregister::registerKey(pthreadKey_);
@@ -730,7 +752,16 @@
  * release the element @id.
  */
 void* ThreadEntry::releaseElement(uint32_t id) {
-  auto rlocked = meta->allId2ThreadEntrySets_[id].rlock();
+  auto& set = meta->allId2ThreadEntrySets_[id];
+
+  // Check if there are pending inserts that need to be processed first.
+  // This must be done before calling release() to ensure synchronization.
+  if (set.hasPendingInserts()) {
+    // Acquire write lock to drain pending inserts before we proceed
+    auto wlocked = set.wlock();
+  }
+
+  auto rlocked = set.rlock();
   auto capacity = getElementsCapacity();
   void* ptrToReturn = (capacity >= id) ? elements[id].release() : nullptr;
   auto slot = rlocked->getIndexFor(this);
@@ -759,7 +790,46 @@
 void ThreadEntry::resetElementImplAfterSet(
     const ElementWrapper& element, uint32_t id) {
   auto& set = meta->allId2ThreadEntrySets_[id];
-  auto rlock = set.rlock();
+
+  // Try to acquire read lock without blocking
+  auto triedRlock = set.tryRLock();
+  if (!triedRlock) {
+    // Failed to acquire the lock. If the old value is null, we can defer
+    // the ThreadEntrySet update.
+    ElementWrapper oldWrapper = elements[id];
+    // The pending inserts optimization is only useful when accessAllThreads()
+    // is available and can cause contention. Void tags don't support
+    // accessAllThreads(), so skip this optimization in that case.
+    if (!removed_ && meta->allowsAccessAllThreads_ &&
+        oldWrapper.ptr == nullptr) {
+      elements[id] = element;
+      // Defer cleanup and ThreadEntrySet update since we don't hold the lock
+      ThreadEntrySet::PendingElement pendingElement;
+      pendingElement.threadEntry = this;
+      pendingElement.wrapper = element;
+      pendingElement.oldWrapper = oldWrapper;
+      pendingElement.elementId = id;
+      set.deferInsert(pendingElement);
+      return;
+    }
+    // Fall back to acquiring the lock since we need to ensure consistency
+  }
+
+  // Acquire a regular rlock. Reuse the try lock if we have it.
+  auto rlock = triedRlock
+      ? StaticMetaBase::SynchronizedThreadEntrySet::RLockedPtr(
+            std::move(triedRlock))
+      : set.rlock();
+
+  // Drain pending inserts before cleanup. This is critical when resetting
+  // to null (!null -> null) to ensure we drain any prior pending insert
+  // for this thread before the caller deletes the element pointer.
+  if (set.hasPendingInserts()) {
+    rlock.unlock();
+    auto wlock = set.wlock(); // Drains pending inserts
+    rlock = wlock.moveFromWriteToRead();
+  }
+
   cleanupElement(id);
   elements[id] = element;
   if (removed_) {
@@ -769,6 +839,8 @@
     return;
   }
   if (element.ptr != nullptr && !rlock->contains(this)) {
+    // Contention can occur here when upgrading from rlock to wlock.
+    // This is a common case during null -> non-null transitions.
     meta->ensureThreadEntryIsInSet(this, set, rlock);
   }
   auto slot = rlock->getIndexFor(this);
diff --git a/folly/folly/detail/ThreadLocalDetail.h b/folly/folly/detail/ThreadLocalDetail.h
--- a/folly/folly/detail/ThreadLocalDetail.h
+++ b/folly/folly/detail/ThreadLocalDetail.h
@@ -285,6 +285,16 @@
     /* implicit */ Element(ThreadEntry* entry = nullptr) : threadEntry(entry) {}
   };
 
+  // struct for deferred insert - only used in pendingInserts.
+  struct PendingElement {
+    ThreadEntry* threadEntry{};
+    ElementDisposeInfo wrapper;
+    // Old wrapper that needs cleanup when this element is inserted
+    ElementDisposeInfo oldWrapper;
+    // Element ID for this wrapper (used when deferring cleanup)
+    uint32_t elementId{0};
+  };
+
   // Vector of ThreadEntry for fast iteration during accessAllThreads.
   using ElementVector = std::vector<Element>;
   ElementVector threadElements;
@@ -372,7 +382,8 @@
     }
   };
 
-  StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict);
+  StaticMetaBase(
+      ThreadEntry* (*threadEntry)(), bool strict, bool allowsAccessAllThreads);
 
   FOLLY_EXPORT static ThreadEntryList* getThreadEntryList();
 
@@ -402,8 +413,102 @@
 
   ElementWrapper& getElement(EntryID* ent);
 
-  using SynchronizedThreadEntrySet = folly::Synchronized<ThreadEntrySet>;
+  /**
+   * Wrapper around Synchronized<ThreadEntrySet> that automatically drains
+   * pending inserts whenever a write lock is acquired.
+   */
+  class SynchronizedThreadEntrySet {
+   public:
+    using Base = folly::Synchronized<ThreadEntrySet>;
+    using RLockedPtr = typename Base::RLockedPtr;
+    using WLockedPtr = typename Base::WLockedPtr;
 
+    SynchronizedThreadEntrySet() = default;
+
+    // Forward rlock() to the underlying Synchronized
+    auto rlock() { return synchronized_.rlock(); }
+    auto rlock() const { return synchronized_.rlock(); }
+
+    // Forward tryRLock() to the underlying Synchronized
+    auto tryRLock() { return synchronized_.tryRLock(); }
+    auto tryRLock() const { return synchronized_.tryRLock(); }
+
+    // wlock() automatically completes pending inserts
+    auto wlock() {
+      auto lock = synchronized_.wlock();
+      completePendingInserts(*lock);
+      return lock;
+    }
+
+    bool hasPendingInserts() const { return pendingInsertsCount_.load() > 0; }
+
+    /**
+     * Add the given element to the pendingInserts_ queue to be drained later.
+     */
+    void deferInsert(const ThreadEntrySet::PendingElement& pendingElement) {
+      auto lock = pendingInserts_.wlock();
+      lock->push_back(pendingElement);
+      incrementPendingInsertsCount();
+    }
+
+   private:
+    /**
+     * Increment the pending inserts count. Called when adding to the
+     * pending inserts queue.
+     */
+    void incrementPendingInsertsCount() { pendingInsertsCount_.fetch_add(1); }
+    /**
+     * Complete pending inserts by draining the pending inserts list and
+     * updating the ThreadEntrySet. This should be called while holding
+     * the write lock on the ThreadEntrySet.
+     */
+    void completePendingInserts(ThreadEntrySet& set) {
+      // Drain pending inserts by swapping out the vector
+      std::vector<ThreadEntrySet::PendingElement> pending;
+      pendingInserts_.wlock()->swap(pending);
+
+      for (auto& pendingElement : pending) {
+        ThreadEntrySet::Element element{pendingElement.threadEntry};
+        element.wrapper = pendingElement.wrapper;
+        auto iter = set.entryToVectorSlot.find(pendingElement.threadEntry);
+        if (iter != set.entryToVectorSlot.end()) {
+          // Entry already present. Update the wrapper.
+          DCHECK_EQ(
+              element.threadEntry,
+              set.threadElements[iter->second].threadEntry);
+          set.threadElements[iter->second].wrapper = pendingElement.wrapper;
+        } else {
+          // Insert new entry
+          set.threadElements.push_back(element);
+          auto idx = set.threadElements.size() - 1;
+          set.entryToVectorSlot.insert(iter, {element.threadEntry, idx});
+        }
+
+        // The old wrapper should always be null
+        DCHECK(pendingElement.oldWrapper.ptr == nullptr);
+      }
+
+      if (!pending.empty()) {
+        [[maybe_unused]] auto oldCount =
+            pendingInsertsCount_.fetch_sub(pending.size());
+        DCHECK_GE(oldCount, pending.size());
+      }
+    }
+
+    Base synchronized_;
+    /**
+     * Holds elements that failed to acquire read lock during resetElement().
+     * This typically occurs due to contention with accessAllThreads(), which
+     * may hold the write lock for extended periods.
+     *
+     * Pending elements are drained via completePendingInserts(), which is
+     * invoked whenever the write lock is acquired on the ThreadEntrySet.
+     */
+    folly::Synchronized<std::vector<ThreadEntrySet::PendingElement>>
+        pendingInserts_;
+    folly::relaxed_atomic<size_t> pendingInsertsCount_{0};
+  };
+
   /*
    * Helper inline methods to add/remove/clear ThreadEntry* from
    * allId2ThreadEntrySets_
@@ -479,9 +584,7 @@
       ThreadEntry* threadEntry, uint32_t idval, size_t& newCapacity);
 
   span<SynchronizedThreadEntrySet* const> getThreadEntrySetsPtrSpan() {
-    const auto sets = allId2ThreadEntrySets_.as_view().as_ptr_span();
-    const size_t nextId = nextId_.load();
-    return sets.subspan(0, std::min(sets.size(), nextId));
+    return allId2ThreadEntrySets_.as_view().as_ptr_span(nextId_.load());
   }
 
   relaxed_atomic_uint32_t nextId_;
@@ -508,6 +611,7 @@
   pthread_key_t pthreadKey_;
   ThreadEntry* (*threadEntry_)();
   bool strict_;
+  bool allowsAccessAllThreads_;
   // Total size of ElementWrapper arrays across all threads. This is meant
   // to surface the overhead of thread local tracking machinery since the array
   // can be sparse when there are lots of thread local variables under the same
@@ -580,7 +684,8 @@
 
  public:
   StaticMeta()
-      : StaticMetaBase(&StaticMeta::getThreadEntrySlow, IsAccessModeStrict) {
+      : StaticMetaBase(
+            &StaticMeta::getThreadEntrySlow, IsAccessModeStrict, !IsTagVoid) {
     AtFork::registerHandler(
         this,
         /*prepare*/ &StaticMeta::preFork,
diff --git a/folly/folly/detail/base64_detail/Base64Api.h b/folly/folly/detail/base64_detail/Base64Api.h
--- a/folly/folly/detail/base64_detail/Base64Api.h
+++ b/folly/folly/detail/base64_detail/Base64Api.h
@@ -98,4 +98,18 @@
   }
 }
 
+inline constexpr const char* base64PHPStrictRemoveWhitespaces(
+    const char* f, const char* l, char* o) noexcept {
+  return base64PHPStrictRemoveWhitespacesScalar(f, l, o);
+}
+
+inline constexpr Base64DecodeResult base64PHPStrictDecode(
+    const char* f, const char* l, char* o) noexcept {
+  const char* o_l = base64PHPStrictRemoveWhitespaces(f, l, o);
+
+  // Encoded string is always longer so reuse output to avoid extra allocation
+  Base64DecodeResult result = base64Decode(o, o_l, o);
+  return {result.isSuccess, result.o};
+}
+
 } // namespace folly::detail::base64_detail
diff --git a/folly/folly/detail/base64_detail/Base64Common.h b/folly/folly/detail/base64_detail/Base64Common.h
--- a/folly/folly/detail/base64_detail/Base64Common.h
+++ b/folly/folly/detail/base64_detail/Base64Common.h
@@ -18,6 +18,8 @@
 
 #include <cstddef>
 #include <cstdint>
+#include <folly/Utility.h>
+#include <folly/detail/base64_detail/Base64Constants.h>
 
 namespace folly::detail::base64_detail {
 
@@ -70,6 +72,14 @@
   res += extra;
 
   return res;
+}
+
+constexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(
+    const char* f, const char* l) {
+  std::size_t n = static_cast<std::size_t>(l - f);
+
+  // Reserve full input size as output is used for temporary storage
+  return n;
 }
 
 struct Base64DecodeResult {
diff --git a/folly/folly/detail/base64_detail/Base64Constants.h b/folly/folly/detail/base64_detail/Base64Constants.h
--- a/folly/folly/detail/base64_detail/Base64Constants.h
+++ b/folly/folly/detail/base64_detail/Base64Constants.h
@@ -62,6 +62,15 @@
   return base64DecodeRule(x);
 }
 
+constexpr std::uint8_t base64PHPStrictDecodeSkipRule(char x) {
+  // This is different from std::isspace and std::isblank in <cctype>
+  if (x == '\t' || x == '\n' || x == '\r' || x == ' ') {
+    return 0;
+  }
+
+  return 1;
+}
+
 template <typename DecodeChar>
 constexpr auto buildDecodeTable(DecodeChar decodeChar) {
   std::array<char, 256> res = {};
@@ -71,9 +80,19 @@
   return res;
 }
 
+constexpr auto buildBase64PHPStrictDecodeSkipTable() {
+  std::array<std::uint8_t, 256> res = {};
+  for (std::size_t i = 0; i != res.size(); ++i) {
+    res[i] = base64PHPStrictDecodeSkipRule(static_cast<char>(i));
+  }
+  return res;
+}
+
 constexpr std::array<char, 256> kBase64DecodeTable =
     buildDecodeTable(base64DecodeRule);
 constexpr std::array<char, 256> kBase64URLDecodeTable =
     buildDecodeTable(base64URLDecodeRule);
+constexpr std::array<std::uint8_t, 256> kBase64PHPStrictDecodeSkipTable =
+    buildBase64PHPStrictDecodeSkipTable();
 
 } // namespace folly::detail::base64_detail::constants
diff --git a/folly/folly/detail/base64_detail/Base64Scalar.h b/folly/folly/detail/base64_detail/Base64Scalar.h
--- a/folly/folly/detail/base64_detail/Base64Scalar.h
+++ b/folly/folly/detail/base64_detail/Base64Scalar.h
@@ -18,8 +18,11 @@
 
 #include <array>
 #include <cstdint>
+#include <folly/Utility.h>
 #include <folly/detail/base64_detail/Base64Common.h>
 #include <folly/detail/base64_detail/Base64Constants.h>
+#include <folly/memory/UninitializedMemoryHacks.h>
+#include <folly/portability/Constexpr.h>
 
 namespace folly::detail::base64_detail {
 
@@ -29,9 +32,9 @@
 
 constexpr std::array<std::uint8_t, 3> base64DecodePack4To3(
     std::uint8_t aaa, std::uint8_t bbb, std::uint8_t ccc, std::uint8_t ddd) {
-  std::uint8_t aaab = (aaa << 2) | (bbb >> 4);
-  std::uint8_t bbcc = (bbb << 4) | (ccc >> 2);
-  std::uint8_t cddd = (ccc << 6) | ddd;
+  std::uint8_t aaab = static_cast<std::uint8_t>((aaa << 2) | (bbb >> 4));
+  std::uint8_t bbcc = static_cast<std::uint8_t>((bbb << 4) | (ccc >> 2));
+  std::uint8_t cddd = static_cast<std::uint8_t>((ccc << 6) | ddd);
 
   return {{aaab, bbcc, cddd}};
 }
@@ -140,7 +143,7 @@
   std::uint8_t aaa = constants::kBase64DecodeTable[atAsU8(f, 0)];
   std::uint8_t bbb = constants::kBase64DecodeTable[atAsU8(f, 1)];
 
-  *o++ = (aaa << 2) | (bbb >> 4);
+  *o++ = static_cast<std::uint8_t>((aaa << 2) | (bbb >> 4));
   errorAccumulator |= aaa | bbb;
 
   if (f[2] == '=' && f[3] == '=') {
@@ -210,7 +213,7 @@
   std::uint8_t aaa = constants::kBase64URLDecodeTable[atAsU8(f, 0)];
   std::uint8_t bbb = constants::kBase64URLDecodeTable[atAsU8(f, 1)];
 
-  *o++ = (aaa << 2) | (bbb >> 4);
+  *o++ = static_cast<std::uint8_t>((aaa << 2) | (bbb >> 4));
   errorAccumulator |= aaa | bbb; // This will detect incorrect padding as well
 
   f += 2;
@@ -257,6 +260,21 @@
       errorAccumulator !=
           static_cast<std::uint8_t>(constants::kDecodeErrorMarker),
       o};
+}
+
+constexpr inline const char* base64PHPStrictRemoveWhitespacesScalar(
+    const char* f, const char* l, char* o) noexcept {
+  constexpr auto& skips =
+      detail::base64_detail::constants::kBase64PHPStrictDecodeSkipTable;
+  std::size_t pos = 0;
+
+  while (f != l) {
+    auto v = *f++;
+    o[pos] = v;
+    pos += skips[to_unsigned(v)];
+  }
+
+  return o + pos;
 }
 
 } // namespace folly::detail::base64_detail
diff --git a/folly/folly/detail/tuple.h b/folly/folly/detail/tuple.h
--- a/folly/folly/detail/tuple.h
+++ b/folly/folly/detail/tuple.h
@@ -124,7 +124,7 @@
 struct entry {
   using entry_type = T;
   [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] T entry_value;
-  constexpr auto operator<=>(const entry&) const = default;
+  auto operator<=>(const entry&) const = default;
 };
 
 template <typename Seq, typename...>
@@ -133,14 +133,14 @@
 template <size_t... Is, typename... Ts>
 struct tuple_base<std::index_sequence<Is...>, Ts...> : entry<Is, Ts>... {
   using tuple_base_list = tag_t<entry<Is, Ts>...>;
-  constexpr auto operator<=>(const tuple_base&) const = default;
+  auto operator<=>(const tuple_base&) const = default;
 };
 
 } // namespace detail
 
 template <typename... Ts>
 struct tuple : detail::tuple_base<std::index_sequence_for<Ts...>, Ts...> {
-  constexpr auto operator<=>(const tuple&) const = default;
+  auto operator<=>(const tuple&) const = default;
 };
 template <typename... Ts>
 tuple(Ts...) -> tuple<Ts...>;
@@ -267,9 +267,9 @@
 } // namespace folly::detail::lite_tuple
 
 namespace folly {
-template <typename... As>
-struct safe_alias_of<::folly::detail::lite_tuple::tuple<As...>>
-    : safe_alias_of_pack<As...> {};
+template <typename... As, safe_alias Default>
+struct safe_alias_of<::folly::detail::lite_tuple::tuple<As...>, Default>
+    : safe_alias_of_pack<Default, As...> {};
 } // namespace folly
 
 namespace std {
diff --git a/folly/folly/dynamic-inl.h b/folly/folly/dynamic-inl.h
--- a/folly/folly/dynamic-inl.h
+++ b/folly/folly/dynamic-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/json/dynamic-inl.h>
+#include <folly/json/dynamic-inl.h> // @shim
diff --git a/folly/folly/dynamic.h b/folly/folly/dynamic.h
--- a/folly/folly/dynamic.h
+++ b/folly/folly/dynamic.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/json/dynamic.h>
+#include <folly/json/dynamic.h> // @shim
diff --git a/folly/folly/executors/CPUThreadPoolExecutor.cpp b/folly/folly/executors/CPUThreadPoolExecutor.cpp
--- a/folly/folly/executors/CPUThreadPoolExecutor.cpp
+++ b/folly/folly/executors/CPUThreadPoolExecutor.cpp
@@ -37,6 +37,12 @@
     true,
     "CPUThreadPoolExecutor will use ThrottledLifoSem by default");
 
+FOLLY_GFLAGS_DEFINE_bool(
+    folly_cputhreadpoolexecutor_always_dequeue_with_timeout,
+    false,
+    "Always use BlockingQueue::try_take_for() even when the pool cannot be "
+    "scaled down. Only for testing purposes");
+
 namespace folly {
 
 const size_t CPUThreadPoolExecutor::kDefaultMaxQueueSize = 1 << 14;
@@ -46,9 +52,7 @@
     std::chrono::milliseconds expiration,
     Func&& expireCallback,
     int8_t pri)
-    : Task(std::move(f), expiration, std::move(expireCallback), pri) {
-  DCHECK(func_); // Empty func reserved as poison.
-}
+    : Task(std::move(f), expiration, std::move(expireCallback), pri) {}
 
 CPUThreadPoolExecutor::CPUTask::CPUTask()
     : Task(nullptr, std::chrono::milliseconds(0), nullptr) {}
@@ -221,7 +225,10 @@
 
 void CPUThreadPoolExecutor::add(
     Func func, std::chrono::milliseconds expiration, Func expireCallback) {
-  addImpl<false>(std::move(func), 0, expiration, std::move(expireCallback));
+  CPUTask task(std::move(func), expiration, std::move(expireCallback), 0);
+  addImpl(
+      [this](auto&& t) { return taskQueue_->add(std::move(t)); },
+      std::move(task));
 }
 
 void CPUThreadPoolExecutor::addWithPriority(Func func, int8_t priority) {
@@ -233,52 +240,15 @@
     int8_t priority,
     std::chrono::milliseconds expiration,
     Func expireCallback) {
-  addImpl<true>(
-      std::move(func), priority, expiration, std::move(expireCallback));
-}
-
-template <bool withPriority>
-void CPUThreadPoolExecutor::addImpl(
-    Func func,
-    int8_t priority,
-    std::chrono::milliseconds expiration,
-    Func expireCallback) {
-  if (!func) {
-    // Reserve empty funcs as poison by logging the error inline.
-    invokeCatchingExns("ThreadPoolExecutor: func", std::move(func));
-    return;
-  }
-
-  if (withPriority) {
-    CHECK_GT(getNumPriorities(), 0);
-  }
-
+  CHECK_GT(getNumPriorities(), 0);
   CPUTask task(
       std::move(func), expiration, std::move(expireCallback), priority);
-  if (auto queueObserver = getQueueObserver(priority)) {
-    task.queueObserverPayload_ = queueObserver->onEnqueued(task.context_.get());
-  }
-  registerTaskEnqueue(task);
-
-  // It's not safe to expect that the executor is alive after a task is added to
-  // the queue (this task could be holding the last KeepAlive and when finished
-  // - it may unblock the executor shutdown).
-  // If we need executor to be alive after adding into the queue, we have to
-  // acquire a KeepAlive.
-  bool mayNeedToAddThreads = minThreads_.load(std::memory_order_relaxed) == 0 ||
-      activeThreads_.load(std::memory_order_relaxed) <
-          maxThreads_.load(std::memory_order_relaxed);
-  folly::Executor::KeepAlive<> ka = mayNeedToAddThreads
-      ? getKeepAliveToken(this)
-      : folly::Executor::KeepAlive<>{};
-
-  auto result = withPriority
-      ? taskQueue_->addWithPriority(std::move(task), priority)
-      : taskQueue_->add(std::move(task));
-
-  if (mayNeedToAddThreads && !result.reusedThread) {
-    ensureActiveThreads();
-  }
+  addImpl(
+      [this](auto&& t) {
+        auto pri = t.priority();
+        return taskQueue_->addWithPriority(std::move(t), pri);
+      },
+      std::move(task));
 }
 
 uint8_t CPUThreadPoolExecutor::getNumPriorities() const {
@@ -298,25 +268,30 @@
   return taskQueue_.get();
 }
 
-// threadListLock_ must be writelocked.
-bool CPUThreadPoolExecutor::tryDecrToStop() {
-  auto toStop = threadsToStop_.load(std::memory_order_relaxed);
-  if (toStop <= 0) {
-    return false;
-  }
-  threadsToStop_.store(toStop - 1, std::memory_order_relaxed);
+// Does not need threadListLock_ lock.
+bool CPUThreadPoolExecutor::shouldStopThread(bool isPoison) {
+  auto threadsToStop = threadsToStop_.load(std::memory_order_relaxed);
+  do {
+    if (threadsToStop == 0 ||
+        // If we're joining, do not allow early stopping: only stop threads when
+        // a poison task is received.
+        (!isPoison && isJoin_.load(std::memory_order_acquire))) {
+      return false;
+    }
+  } while (!threadsToStop_.compare_exchange_weak(
+      threadsToStop, threadsToStop - 1, std::memory_order_relaxed));
   return true;
 }
 
-bool CPUThreadPoolExecutor::taskShouldStop(folly::Optional<CPUTask>& task) {
-  if (tryDecrToStop()) {
-    return true;
-  }
-  if (task) {
-    return false;
-  } else {
-    return tryTimeoutThread();
+// threadListLock_ must be writelocked.
+void CPUThreadPoolExecutor::stopThread(const ThreadPtr& thread) {
+  for (auto& o : observers_) {
+    o->threadStopped(thread.get());
   }
+  stoppedThreadProcessedTasks_ += thread->processedTasks;
+  thread->processedTasks = 0;
+  threadList_.remove(thread);
+  stoppedThreads_.add(folly::copy(thread));
 }
 
 void CPUThreadPoolExecutor::threadRun(ThreadPtr thread) {
@@ -337,24 +312,23 @@
     threadIdCollector_->removeTid(folly::getOSThreadID());
   });
   while (true) {
-    auto task = taskQueue_->try_take_for(
-        threadTimeout_.load(std::memory_order_relaxed));
+    auto task =
+        (threadsCanTimeout_.load(std::memory_order_relaxed) ||
+         FLAGS_folly_cputhreadpoolexecutor_always_dequeue_with_timeout)
+        ? taskQueue_->try_take_for(
+              threadTimeout_.load(std::memory_order_relaxed))
+        : taskQueue_->take();
 
-    // Handle thread stopping, either by task timeout, or
-    // by 'poison' task added in join() or stop().
-    if (FOLLY_UNLIKELY(!task || !task->func_)) {
-      // Actually remove the thread from the list.
+    // Handle thread stopping, either by task timeout, or by 'poison' task added
+    // by stopThreads().
+    if (bool timeout = !task; FOLLY_UNLIKELY(timeout || !task->func_)) {
       std::unique_lock w{threadListLock_};
-      if (taskShouldStop(task)) {
-        for (auto& o : observers_) {
-          o->threadStopped(thread.get());
-        }
-        threadList_.remove(thread);
-        stoppedThreads_.add(thread);
+      if (shouldStopThread(/* isPoison */ !timeout) ||
+          (timeout && tryTimeoutThread())) {
+        stopThread(thread);
         return;
-      } else {
-        continue;
       }
+      continue;
     }
 
     if (auto queueObserver = getQueueObserver(task->priority())) {
@@ -362,13 +336,10 @@
     }
     runTask(thread, std::move(task.value()));
 
-    if (FOLLY_UNLIKELY(threadsToStop_ > 0 && !isJoin_)) {
+    if (shouldStopThread(/* isPoison */ false)) {
       std::unique_lock w{threadListLock_};
-      if (tryDecrToStop()) {
-        threadList_.remove(thread);
-        stoppedThreads_.add(thread);
-        return;
-      }
+      stopThread(thread);
+      return;
     }
   }
 }
diff --git a/folly/folly/executors/CPUThreadPoolExecutor.h b/folly/folly/executors/CPUThreadPoolExecutor.h
--- a/folly/folly/executors/CPUThreadPoolExecutor.h
+++ b/folly/folly/executors/CPUThreadPoolExecutor.h
@@ -30,34 +30,27 @@
 /**
  * A Thread pool for CPU bound tasks.
  *
- * @note A single queue backed by folly/LifoSem and folly/MPMC queue.
- * Because of this contention can be quite high,
- * since all the worker threads and all the producer threads hit
- * the same queue. MPMC queue excels in this situation but dictates a max queue
- * size.
- *
- * @note The default queue throws when full (folly::QueueBehaviorIfFull::THROW),
- * so add() can fail. Furthermore, join() can also fail if the queue is full,
- * because it enqueues numThreads poison tasks to stop the threads. If join() is
- * needed to be guaranteed to succeed PriorityLifoSemMPMCQueue can be used
- * instead, initializing the lowest priority's (LO_PRI) capacity to at least
- * numThreads. Poisons use LO_PRI so if that priority is not used for any user
- * task join() is guaranteed not to encounter a full queue.
+ * @note A single queue backed by:
+ * - An efficient semaphore, such as:
+ *   - folly::LifoSem
+ *   - folly::ThrottledlifoSem
+ * * An efficient unbounded concurrent queue, such as:
+ *   - folly::UMPMCQueue
+ * Therefore, this thread pool scales to very high levels of concurrent access.
  *
- * @note If a blocking queue (folly::QueueBehaviorIfFull::BLOCK) is used, and
+ * @note If a bounded queue (folly::QueueBehaviorIfFull::BLOCK) is used, and
  * tasks executing on a given thread pool schedule more tasks, deadlock is
- * possible if the queue becomes full.  Deadlock is also possible if there is
+ * possible if the queue becomes full. Deadlock is also possible if there is
  * a circular dependency among multiple thread pools with blocking queues.
- * To avoid this situation, use non-blocking queue(s), or schedule tasks only
- * from threads not belonging to the given thread pool(s), or use
- * folly::IOThreadPoolExecutor.
+ * To avoid this situation, either use non-blocking queue(s) only (default and
+ * recommended), or schedule tasks only from threads not belonging to the given
+ * thread pool(s).
  *
- * @note LifoSem wakes up threads in Lifo order - i.e. there are only few
- * threads as necessary running, and we always try to reuse the same few threads
- * for better cache locality.
- * All Folly BlockingQueue implementations use either LifoSem or
- * ThrottledLifoSem, which madvise away the stack of threads that are inactive
- * for a long time.
+ * @note LifoSem and ThrottledLifoSem wake up threads in LIFO order - i.e. there
+ * are only ever as few threads as necessary actually running, and we always try
+ * to reuse the same few threads for better cache locality. The other threads
+ * are be suspended continuously until they are needed to handle spikes in work,
+ * and their stacks would be madvised away while the threads are suspended.
  *
  * @note Supports priorities - priorities are implemented as multiple queues -
  * each worker thread checks the highest priority queue first. Threads
@@ -185,6 +178,9 @@
 
  protected:
   BlockingQueue<CPUTask>* FOLLY_NONNULL getTaskQueue();
+  template <typename EnqueueTask>
+  void addImpl(EnqueueTask&& enqueueTask, CPUTask&& task);
+
   std::unique_ptr<ThreadIdWorkerProvider> threadIdCollector_{
       std::make_unique<ThreadIdWorkerProvider>()};
 
@@ -193,15 +189,8 @@
   void stopThreads(size_t n) override;
   size_t getPendingTaskCountImpl() const override final;
 
-  bool tryDecrToStop();
-  bool taskShouldStop(folly::Optional<CPUTask>&);
-
-  template <bool withPriority>
-  void addImpl(
-      Func func,
-      int8_t priority,
-      std::chrono::milliseconds expiration,
-      Func expireCallback);
+  bool shouldStopThread(bool isPoison);
+  void stopThread(const ThreadPtr& thread);
 
   std::unique_ptr<folly::QueueObserverFactory> createQueueObserverFactory();
   QueueObserver* FOLLY_NULLABLE getQueueObserver(int8_t pri);
@@ -211,8 +200,40 @@
   std::array<std::atomic<folly::QueueObserver*>, UCHAR_MAX + 1> queueObservers_;
   std::unique_ptr<folly::QueueObserverFactory> queueObserverFactory_{
       createQueueObserverFactory()};
-  std::atomic<ssize_t> threadsToStop_{0};
+  std::atomic<size_t> threadsToStop_{0};
   Options::Blocking prohibitBlockingOnThreadPools_ = Options::Blocking::allow;
 };
+
+template <typename EnqueueTask>
+void CPUThreadPoolExecutor::addImpl(EnqueueTask&& enqueueTask, CPUTask&& task) {
+  if (!task.func_) {
+    // Reserve empty funcs as poison by logging the error inline.
+    invokeCatchingExns("ThreadPoolExecutor: func", std::move(task.func_));
+    return;
+  }
+
+  if (auto queueObserver = getQueueObserver(task.priority())) {
+    task.queueObserverPayload_ = queueObserver->onEnqueued(task.context_.get());
+  }
+  registerTaskEnqueue(task);
+
+  // It's not safe to expect that the executor is alive after a task is added to
+  // the queue (this task could be holding the last KeepAlive and when finished
+  // - it may unblock the executor shutdown).
+  // If we need executor to be alive after adding into the queue, we have to
+  // acquire a KeepAlive.
+  bool mayNeedToAddThreads = minThreads_.load(std::memory_order_relaxed) == 0 ||
+      activeThreads_.load(std::memory_order_relaxed) <
+          maxThreads_.load(std::memory_order_relaxed);
+  folly::Executor::KeepAlive<> ka = mayNeedToAddThreads
+      ? getKeepAliveToken(this)
+      : folly::Executor::KeepAlive<>{};
+
+  auto result = enqueueTask(std::move(task));
+
+  if (mayNeedToAddThreads && !result.reusedThread) {
+    ensureActiveThreads();
+  }
+}
 
 } // namespace folly
diff --git a/folly/folly/executors/Codel.cpp b/folly/folly/executors/Codel.cpp
--- a/folly/folly/executors/Codel.cpp
+++ b/folly/folly/executors/Codel.cpp
@@ -31,9 +31,10 @@
 namespace folly {
 
 Codel::Codel()
-    : Codel(Codel::Options()
-                .setInterval(milliseconds(FLAGS_codel_interval))
-                .setTargetDelay(milliseconds(FLAGS_codel_target_delay))) {}
+    : Codel(
+          Codel::Options()
+              .setInterval(milliseconds(FLAGS_codel_interval))
+              .setTargetDelay(milliseconds(FLAGS_codel_target_delay))) {}
 
 Codel::Codel(const Options& options)
     : codelMinDelayNs_(0),
diff --git a/folly/folly/executors/EDFThreadPoolExecutor.cpp b/folly/folly/executors/EDFThreadPoolExecutor.cpp
--- a/folly/folly/executors/EDFThreadPoolExecutor.cpp
+++ b/folly/folly/executors/EDFThreadPoolExecutor.cpp
@@ -29,7 +29,6 @@
 #include <vector>
 
 #include <glog/logging.h>
-#include <folly/ScopeGuard.h>
 #include <folly/concurrency/ProcessLocalUniqueId.h>
 #include <folly/portability/GFlags.h>
 #include <folly/synchronization/LifoSem.h>
@@ -45,15 +44,23 @@
 
 class EDFThreadPoolExecutor::Task {
  public:
-  explicit Task(Func&& f, int repeat, uint64_t deadline)
-      : f_(std::move(f)), total_(repeat), deadline_(deadline) {}
+  struct Poison {};
 
+  explicit Task(Func&& f, uint64_t deadline)
+      : f_(std::move(f)), total_(1), deadline_(deadline) {}
+
   explicit Task(std::vector<Func>&& fs, uint64_t deadline)
       : fs_(std::move(fs)), total_(fs_.size()), deadline_(deadline) {}
 
+  explicit Task(Poison, int total) : total_(total), deadline_(kLatestDeadline) {
+    CHECK_GT(total, 0);
+  }
+
   uint64_t getDeadline() const { return deadline_; }
   uint64_t getEnqueueOrder() const { return enqueueOrder_; }
 
+  bool isPoison() const { return !f_ && fs_.empty() && total_ > 0; }
+
   bool isDone() const {
     return iter_.load(std::memory_order_relaxed) >= total_;
   }
@@ -89,7 +96,7 @@
   std::atomic<int> iter_{0};
   int total_;
   uint64_t deadline_;
-  uint64_t enqueueOrder_;
+  uint64_t enqueueOrder_ = 0;
   std::shared_ptr<RequestContext> context_ = RequestContext::saveContext();
   std::chrono::steady_clock::time_point enqueueTime_ =
       std::chrono::steady_clock::now();
@@ -120,9 +127,6 @@
 
   static constexpr std::size_t kNumBuckets = 2 << 5;
 
-  explicit TaskQueue()
-      : buckets_{}, curDeadline_(kLatestDeadline), numItems_(0) {}
-
   void push(TaskPtr task) {
     auto deadline = task->getDeadline();
     auto& bucket = getBucket(deadline);
@@ -133,8 +137,6 @@
       bucket.empty.store(bucket.tasks.empty(), std::memory_order_relaxed);
     }
 
-    numItems_.fetch_add(1, std::memory_order_seq_cst);
-
     // Update current earliest deadline if necessary
     uint64_t curDeadline = curDeadline_.load(std::memory_order_relaxed);
     do {
@@ -145,13 +147,10 @@
         curDeadline, deadline, std::memory_order_relaxed));
   }
 
+  // Should only be called on a nonempty queue.
   TaskPtr pop() {
     bool needDeadlineUpdate = false;
     for (;;) {
-      if (numItems_.load(std::memory_order_seq_cst) == 0) {
-        return nullptr;
-      }
-
       auto curDeadline = curDeadline_.load(std::memory_order_relaxed);
       auto& bucket = getBucket(curDeadline);
 
@@ -191,7 +190,6 @@
           // Current task finished. Remove from the queue.
           bucket.tasks.pop();
           bucket.empty.store(bucket.tasks.empty(), std::memory_order_relaxed);
-          numItems_.fetch_sub(1, std::memory_order_seq_cst);
         }
       }
 
@@ -201,8 +199,6 @@
     }
   }
 
-  std::size_t size() const { return numItems_.load(std::memory_order_seq_cst); }
-
  private:
   Bucket& getBucket(uint64_t deadline) {
     return buckets_[deadline % kNumBuckets];
@@ -251,13 +247,7 @@
   }
 
   std::array<Bucket, kNumBuckets> buckets_;
-  std::atomic<uint64_t> curDeadline_;
-
-  // All operations performed on `numItems_` explicitly specify memory
-  // ordering of `std::memory_order_seq_cst`. This is due to `numItems_`
-  // performing Dekker's algorithm with `numIdleThreads_` prior to consumer
-  // threads (workers) wait on `sem_`.
-  std::atomic<std::size_t> numItems_;
+  std::atomic<uint64_t> curDeadline_ = kLatestDeadline;
 };
 
 /* static */ std::unique_ptr<EDFThreadPoolSemaphore>
@@ -300,21 +290,11 @@
   add(std::move(f), kLatestDeadline);
 }
 
-void EDFThreadPoolExecutor::add(Func f, std::size_t total, uint64_t deadline) {
-  if (FOLLY_UNLIKELY(isJoin_.load(std::memory_order_relaxed) || total == 0)) {
-    return;
-  }
-
-  auto task = std::make_shared<Task>(std::move(f), total, deadline);
+void EDFThreadPoolExecutor::add(Func f, uint64_t deadline) {
+  auto task = std::make_shared<Task>(std::move(f), deadline);
   registerTaskEnqueue(*task);
   taskQueue_->push(std::move(task));
-
-  auto numIdleThreads = numIdleThreads_.load(std::memory_order_seq_cst);
-  if (numIdleThreads > 0) {
-    // If idle threads are available notify them, otherwise all worker threads
-    // are running and will get around to this task in time.
-    sem_->post(std::min(total, numIdleThreads));
-  }
+  sem_->post(1);
 }
 
 void EDFThreadPoolExecutor::add(std::vector<Func> fs, uint64_t deadline) {
@@ -326,19 +306,34 @@
   auto task = std::make_shared<Task>(std::move(fs), deadline);
   registerTaskEnqueue(*task);
   taskQueue_->push(std::move(task));
-
-  auto numIdleThreads = numIdleThreads_.load(std::memory_order_seq_cst);
-  if (numIdleThreads > 0) {
-    // If idle threads are available notify them, otherwise all worker threads
-    // are running and will get around to this task in time.
-    sem_->post(std::min(total, numIdleThreads));
-  }
+  sem_->post(static_cast<uint32_t>(total));
 }
 
 size_t EDFThreadPoolExecutor::getTaskQueueSize() const {
-  return taskQueue_->size();
+  return sem_->valueGuess();
 }
 
+bool EDFThreadPoolExecutor::tryStopThread(
+    const ThreadPtr& thread, bool isPoison) {
+  auto threadsToStop = threadsToStop_.load(std::memory_order_relaxed);
+  do {
+    if (threadsToStop == 0 || (!isPoison && isJoin_)) {
+      return false;
+    }
+  } while (!threadsToStop_.compare_exchange_weak(
+      threadsToStop, threadsToStop - 1, std::memory_order_relaxed));
+
+  std::unique_lock w{threadListLock_};
+  for (auto& o : observers_) {
+    o->threadStopped(thread.get());
+  }
+  stoppedThreadProcessedTasks_ += thread->processedTasks;
+  thread->processedTasks = 0;
+  threadList_.remove(thread);
+  stoppedThreads_.add(folly::copy(thread));
+  return true;
+}
+
 void EDFThreadPoolExecutor::threadRun(ThreadPtr thread) {
   this->threadPoolHook_.registerThread();
   ExecutorBlockingGuard guard{
@@ -346,24 +341,23 @@
 
   thread->startupBaton.post();
   for (;;) {
-    auto task = take();
+    sem_->wait();
 
-    // Handle thread stopping
-    if (FOLLY_UNLIKELY(!task)) {
-      // Actually remove the thread from the list.
-      std::unique_lock w{threadListLock_};
-      for (auto& o : observers_) {
-        o->threadStopped(thread.get());
-      }
-      threadList_.remove(thread);
-      stoppedThreads_.add(thread);
-      return;
-    }
+    // We consumed a post so the queue is non-empty, but we need to discard
+    // finished tasks.
+    int iter;
+    std::shared_ptr<EDFThreadPoolExecutor::Task> task;
+    do {
+      task = taskQueue_->pop();
+      iter = task->next();
+    } while (iter < 0);
 
-    int iter = task->next();
-    if (FOLLY_UNLIKELY(iter < 0)) {
-      // This task is already finished
-      continue;
+    if (task->isPoison()) {
+      if (tryStopThread(thread, /* isPoison */ true)) {
+        return;
+      } else {
+        continue; // Poison was consumed early by tryStopThread().
+      }
     }
 
     thread->idle.store(false, std::memory_order_relaxed);
@@ -401,75 +395,32 @@
       observer.taskProcessed(taskInfo);
     });
 
+    thread->processedTasks = thread->processedTasks + 1;
+
     thread->idle.store(true, std::memory_order_relaxed);
     thread->lastActiveTime.store(
         std::chrono::steady_clock::now(), std::memory_order_relaxed);
+
+    if (tryStopThread(thread, /* isPoison */ false)) {
+      return;
+    }
   }
 }
 
 // threadListLock_ is writelocked.
 void EDFThreadPoolExecutor::stopThreads(std::size_t numThreads) {
+  if (numThreads == 0) {
+    return;
+  }
   threadsToStop_.fetch_add(numThreads, std::memory_order_relaxed);
+  taskQueue_->push(
+      std::make_shared<Task>(Task::Poison{}, static_cast<int>(numThreads)));
   sem_->post(numThreads);
 }
 
 // threadListLock_ is read (or write) locked.
 std::size_t EDFThreadPoolExecutor::getPendingTaskCountImpl() const {
   return getTaskQueueSize();
-}
-
-bool EDFThreadPoolExecutor::shouldStop() {
-  // in normal cases, only do a read (prevents cache line bounces)
-  if (threadsToStop_.load(std::memory_order_relaxed) <= 0 ||
-      isJoin_.load(std::memory_order_relaxed)) {
-    return false;
-  }
-  // modify only if needed
-  if (threadsToStop_.fetch_sub(1, std::memory_order_relaxed) > 0) {
-    return true;
-  } else {
-    threadsToStop_.fetch_add(1, std::memory_order_relaxed);
-    return false;
-  }
-}
-
-std::shared_ptr<EDFThreadPoolExecutor::Task> EDFThreadPoolExecutor::take() {
-  if (FOLLY_UNLIKELY(shouldStop())) {
-    return nullptr;
-  }
-
-  if (auto task = taskQueue_->pop()) {
-    return task;
-  }
-
-  if (FOLLY_UNLIKELY(isJoin_.load(std::memory_order_relaxed))) {
-    return nullptr;
-  }
-
-  // No tasks on the horizon, so go sleep
-  numIdleThreads_.fetch_add(1, std::memory_order_seq_cst);
-
-  SCOPE_EXIT {
-    numIdleThreads_.fetch_sub(1, std::memory_order_seq_cst);
-  };
-
-  for (;;) {
-    if (FOLLY_UNLIKELY(shouldStop())) {
-      return nullptr;
-    }
-
-    if (auto task = taskQueue_->pop()) {
-      // It's possible to return a finished task here, in which case
-      // the worker will call this function again.
-      return task;
-    }
-
-    if (FOLLY_UNLIKELY(isJoin_.load(std::memory_order_relaxed))) {
-      return nullptr;
-    }
-
-    sem_->wait();
-  }
 }
 
 void EDFThreadPoolExecutor::fillTaskInfo(const Task& task, TaskInfo& info) {
diff --git a/folly/folly/executors/EDFThreadPoolExecutor.h b/folly/folly/executors/EDFThreadPoolExecutor.h
--- a/folly/folly/executors/EDFThreadPoolExecutor.h
+++ b/folly/folly/executors/EDFThreadPoolExecutor.h
@@ -32,6 +32,7 @@
   virtual ~EDFThreadPoolSemaphore() = default;
   virtual void post(uint32_t value) = 0;
   virtual void wait() = 0;
+  virtual uint32_t valueGuess() const = 0;
 };
 
 template <class Semaphore>
@@ -43,6 +44,7 @@
 
   void post(uint32_t value) override { sem_.post(value); }
   void wait() override { sem_.wait(); }
+  uint32_t valueGuess() const override { return sem_.valueGuess(); }
 
  private:
   Semaphore sem_;
@@ -78,11 +80,10 @@
 
   ~EDFThreadPoolExecutor() override;
 
-  using SoftRealTimeExecutor::add;
   using ThreadPoolExecutor::add;
 
   void add(Func f) override;
-  void add(Func f, std::size_t total, uint64_t deadline) override;
+  void add(Func f, uint64_t deadline) override;
   void add(std::vector<Func> fs, uint64_t deadline) override;
 
   size_t getTaskQueueSize() const;
@@ -93,8 +94,7 @@
   std::size_t getPendingTaskCountImpl() const override final;
 
  private:
-  bool shouldStop();
-  std::shared_ptr<Task> take();
+  bool tryStopThread(const ThreadPtr& thread, bool isPoison);
 
   void fillTaskInfo(const Task& task, TaskInfo& info);
   void registerTaskEnqueue(const Task& task);
@@ -102,12 +102,6 @@
   std::unique_ptr<TaskQueue> taskQueue_;
   std::unique_ptr<EDFThreadPoolSemaphore> sem_;
   std::atomic<int> threadsToStop_{0};
-
-  // All operations performed on `numIdleThreads_` explicitly specify memory
-  // ordering of `std::memory_order_seq_cst`. This is due to `numIdleThreads_`
-  // performing Dekker's algorithm with `numItems` prior to consumer threads
-  // (workers) wait on `sem_`.
-  std::atomic<std::size_t> numIdleThreads_{0};
 };
 
 } // namespace folly
diff --git a/folly/folly/executors/ExecutionObserver.h b/folly/folly/executors/ExecutionObserver.h
--- a/folly/folly/executors/ExecutionObserver.h
+++ b/folly/folly/executors/ExecutionObserver.h
@@ -41,9 +41,8 @@
   };
   // Constant time size = false to support auto_unlink behavior, options are
   // mutually exclusive
-  typedef boost::intrusive::
-      list<ExecutionObserver, boost::intrusive::constant_time_size<false>>
-          List;
+  using List = boost::intrusive::
+      list<ExecutionObserver, boost::intrusive::constant_time_size<false>>;
 
   virtual ~ExecutionObserver() = default;
 
diff --git a/folly/folly/executors/FunctionScheduler.cpp b/folly/folly/executors/FunctionScheduler.cpp
--- a/folly/folly/executors/FunctionScheduler.cpp
+++ b/folly/folly/executors/FunctionScheduler.cpp
@@ -245,13 +245,19 @@
   auto it = functionsMap_.find(nameID);
   // check if the nameID is unique
   if (it != functionsMap_.end()) {
-    throw std::invalid_argument(to<std::string>(
-        "FunctionScheduler: a function named \"", nameID, "\" already exists"));
+    throw std::invalid_argument(
+        to<std::string>(
+            "FunctionScheduler: a function named \"",
+            nameID,
+            "\" already exists"));
   }
 
   if (currentFunction_ && currentFunction_->name == nameID) {
-    throw std::invalid_argument(to<std::string>(
-        "FunctionScheduler: a function named \"", nameID, "\" already exists"));
+    throw std::invalid_argument(
+        to<std::string>(
+            "FunctionScheduler: a function named \"",
+            nameID,
+            "\" already exists"));
   }
 
   addFunctionToHeap(
@@ -426,6 +432,18 @@
   return true;
 }
 
+// Use a dedicated method for sleeping in the main loop, so it can be identified
+// easily in profiles and traces.
+FOLLY_NOINLINE void FunctionScheduler::waitForWork(
+    std::unique_lock<std::mutex>& lock) {
+  runningCondvar_.wait(lock);
+}
+FOLLY_NOINLINE void FunctionScheduler::waitForWork(
+    std::unique_lock<std::mutex>& lock,
+    std::chrono::steady_clock::duration timeout) {
+  runningCondvar_.wait_for(lock, timeout);
+}
+
 void FunctionScheduler::run() {
   std::unique_lock lock(mutex_);
 
@@ -435,7 +453,7 @@
     // If we have nothing to run, wait until a function is added or until we
     // are stopped.
     if (functions_.empty()) {
-      runningCondvar_.wait(lock);
+      waitForWork(lock);
       continue;
     }
 
@@ -446,7 +464,7 @@
       runOneFunction(lock, now);
       runningCondvar_.notify_all();
     } else {
-      runningCondvar_.wait_for(lock, sleepTime);
+      waitForWork(lock, sleepTime);
     }
   }
 }
diff --git a/folly/folly/executors/FunctionScheduler.h b/folly/folly/executors/FunctionScheduler.h
--- a/folly/folly/executors/FunctionScheduler.h
+++ b/folly/folly/executors/FunctionScheduler.h
@@ -349,6 +349,11 @@
 
   void clearHeap();
 
+  void waitForWork(std::unique_lock<std::mutex>& lock);
+  void waitForWork(
+      std::unique_lock<std::mutex>& lock,
+      std::chrono::steady_clock::duration timeout);
+
   std::thread thread_;
 
   // Mutex to protect our member variables.
diff --git a/folly/folly/executors/GlobalExecutor.cpp b/folly/folly/executors/GlobalExecutor.cpp
--- a/folly/folly/executors/GlobalExecutor.cpp
+++ b/folly/folly/executors/GlobalExecutor.cpp
@@ -59,7 +59,7 @@
 Singleton<std::shared_ptr<ImmutableGlobalCPUExecutor>, GlobalTag>
     gImmutableGlobalCPUExecutor([] {
       size_t nthreads = FLAGS_folly_global_cpu_executor_threads;
-      nthreads = nthreads ? nthreads : folly::hardware_concurrency();
+      nthreads = nthreads ? nthreads : folly::available_concurrency();
       return new std::shared_ptr<ImmutableGlobalCPUExecutor>(
           new ImmutableGlobalCPUExecutor(
               nthreads,
@@ -69,7 +69,7 @@
 Singleton<std::shared_ptr<IOThreadPoolExecutor>, GlobalTag>
     gImmutableGlobalIOExecutor([] {
       size_t nthreads = FLAGS_folly_global_io_executor_threads;
-      nthreads = nthreads ? nthreads : folly::hardware_concurrency();
+      nthreads = nthreads ? nthreads : folly::available_concurrency();
       return new std::shared_ptr<IOThreadPoolExecutor>(new IOThreadPoolExecutor(
           nthreads,
           std::make_shared<NamedThreadFactory>("GlobalIOThreadPool")));
diff --git a/folly/folly/executors/IOObjectCache.h b/folly/folly/executors/IOObjectCache.h
--- a/folly/folly/executors/IOObjectCache.h
+++ b/folly/folly/executors/IOObjectCache.h
@@ -43,7 +43,7 @@
 template <class T>
 class IOObjectCache {
  public:
-  typedef std::function<std::shared_ptr<T>(folly::EventBase*)> TFactory;
+  using TFactory = std::function<std::shared_ptr<T>(folly::EventBase*)>;
 
   IOObjectCache() = default;
   explicit IOObjectCache(TFactory factory) : factory_(std::move(factory)) {}
diff --git a/folly/folly/executors/IOThreadPoolExecutor.cpp b/folly/folly/executors/IOThreadPoolExecutor.cpp
--- a/folly/folly/executors/IOThreadPoolExecutor.cpp
+++ b/folly/folly/executors/IOThreadPoolExecutor.cpp
@@ -66,9 +66,11 @@
 
         idleTimeout = MemoryIdler::getVariationTimeout(idleTimeout);
 
-        scheduleTimeout(static_cast<uint32_t>(
-            std::chrono::duration_cast<std::chrono::milliseconds>(idleTimeout)
-                .count()));
+        scheduleTimeout(
+            static_cast<uint32_t>(
+                std::chrono::duration_cast<std::chrono::milliseconds>(
+                    idleTimeout)
+                    .count()));
       } else {
         num_++;
       }
@@ -286,7 +288,9 @@
     }
   }
   for (const auto& thread : stoppedThreads) {
-    stoppedThreads_.add(thread);
+    stoppedThreadProcessedTasks_ += thread->processedTasks;
+    thread->processedTasks = 0;
+    stoppedThreads_.add(folly::copy(thread));
     threadList_.remove(thread);
   }
 }
diff --git a/folly/folly/executors/ManualExecutor.h b/folly/folly/executors/ManualExecutor.h
--- a/folly/folly/executors/ManualExecutor.h
+++ b/folly/folly/executors/ManualExecutor.h
@@ -131,9 +131,22 @@
   }
 
   void keepAliveRelease() noexcept override {
-    if (keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
-      add([] {});
-    }
+    auto keepAliveCount = keepAliveCount_.load(std::memory_order_relaxed);
+    do {
+      DCHECK(keepAliveCount > 0);
+      if (keepAliveCount == 1) {
+        add([this] {
+          // the final count *must* be released from this executor or else if we
+          // are mid-destructor we have a data race
+          keepAliveCount_.fetch_sub(1, std::memory_order_relaxed);
+        });
+        return;
+      }
+    } while (!keepAliveCount_.compare_exchange_weak(
+        keepAliveCount,
+        keepAliveCount - 1,
+        std::memory_order_release,
+        std::memory_order_relaxed));
   }
 
  private:
diff --git a/folly/folly/executors/ScheduledExecutor.h b/folly/folly/executors/ScheduledExecutor.h
--- a/folly/folly/executors/ScheduledExecutor.h
+++ b/folly/folly/executors/ScheduledExecutor.h
@@ -29,8 +29,8 @@
  public:
   // Reality is that better than millisecond resolution is very hard to
   // achieve. However, we reserve the right to be incredible.
-  typedef std::chrono::microseconds Duration;
-  typedef std::chrono::steady_clock::time_point TimePoint;
+  using Duration = std::chrono::microseconds;
+  using TimePoint = std::chrono::steady_clock::time_point;
 
   ~ScheduledExecutor() override = default;
 
diff --git a/folly/folly/executors/SerialExecutor.h b/folly/folly/executors/SerialExecutor.h
--- a/folly/folly/executors/SerialExecutor.h
+++ b/folly/folly/executors/SerialExecutor.h
@@ -20,7 +20,6 @@
 #include <memory>
 #include <mutex>
 
-#include <folly/concurrency/UnboundedQueue.h>
 #include <folly/executors/GlobalExecutor.h>
 #include <folly/executors/SerializedExecutor.h>
 #include <folly/io/async/Request.h>
@@ -70,6 +69,8 @@
   static KeepAlive<SerialExecutorImpl> create(
       KeepAlive<Executor> parent = getGlobalCPUExecutor());
 
+  const KeepAlive<Executor>& parent() const { return parent_; }
+
   class Deleter {
    public:
     Deleter() {}
@@ -135,46 +136,20 @@
   Queue<Task> queue_;
 };
 
-template <int LgQueueSegmentSize = 8>
-struct SerialExecutorWithUnboundedQueue {
-  // The consumer should only dequeue when the queue is non-empty, so we don't
-  // need blocking.
-  template <typename Task>
-  using queue =
-      folly::UMPSCQueue<Task, /* MayBlock */ false, LgQueueSegmentSize>;
-  using type = SerialExecutorImpl<queue>;
-};
-
 class NoopMutex;
 
 template <class Task, class Mutex = folly::DistributedMutex>
 class SerialExecutorMPSCQueue;
 
 template <typename Task>
-using SmallSerialExecutorQueue = SerialExecutorMPSCQueue<Task>;
+using SerialExecutorQueue = SerialExecutorMPSCQueue<Task>;
 
 template <typename Task>
 using SPSerialExecutorQueue = SerialExecutorMPSCQueue<Task, NoopMutex>;
 
 } // namespace detail
 
-using SerialExecutor =
-    typename detail::SerialExecutorWithUnboundedQueue<>::type;
-
-template <int LgQueueSegmentSize>
-using SerialExecutorWithLgSegmentSize =
-    typename detail::SerialExecutorWithUnboundedQueue<LgQueueSegmentSize>::type;
-
-/**
- * SerialExecutor implementation that uses a mutex-protected queue. This uses
- * significantly less memory than SerialExecutor, at the expense of being more
- * susceptible to contention on add(). This is intended for use cases where
- * granular SerialExecutors are required, for example one per request. In these
- * scenarios, there are not many concurrent submitters, so contention is not an
- * issue, while memory overhead is.
- */
-using SmallSerialExecutor =
-    detail::SerialExecutorImpl<detail::SmallSerialExecutorQueue>;
+using SerialExecutor = detail::SerialExecutorImpl<detail::SerialExecutorQueue>;
 
 /**
  * Single-producer version of SmallExecutor. It is the responsibility of the
diff --git a/folly/folly/executors/SoftRealTimeExecutor.h b/folly/folly/executors/SoftRealTimeExecutor.h
--- a/folly/folly/executors/SoftRealTimeExecutor.h
+++ b/folly/folly/executors/SoftRealTimeExecutor.h
@@ -29,7 +29,7 @@
 // means that not every deadline is guaranteed to be met.
 class SoftRealTimeExecutor : public virtual Executor {
  public:
-  void add(Func) override = 0;
+  using Executor::add;
 
   // Add a task with an assigned abstract deadline.
   //
@@ -40,11 +40,7 @@
   // rate-monotonic scheduling that prioritizes small tasks. It also enables,
   // for example, tiered scheduling (strictly prioritizing a category of tasks)
   // by assigning the high-bit of the deadline.
-  void add(Func func, uint64_t deadline) {
-    add(std::move(func), /* total */ 1, deadline);
-  }
-
-  virtual void add(Func, std::size_t total, uint64_t deadline) = 0;
+  virtual void add(Func func, uint64_t deadline) = 0;
   virtual void add(std::vector<Func>, uint64_t deadline) = 0;
 
   folly::Executor::KeepAlive<> deadlineExecutor(uint64_t deadline);
diff --git a/folly/folly/executors/StrandExecutor.cpp b/folly/folly/executors/StrandExecutor.cpp
--- a/folly/folly/executors/StrandExecutor.cpp
+++ b/folly/folly/executors/StrandExecutor.cpp
@@ -60,20 +60,22 @@
 }
 
 void StrandContext::add(Func func, Executor::KeepAlive<> executor) {
-  addImpl(QueueItem{
-      std::move(func),
-      std::move(executor),
-      folly::none,
-      RequestContext::saveContext()});
+  addImpl(
+      QueueItem{
+          std::move(func),
+          std::move(executor),
+          folly::none,
+          RequestContext::saveContext()});
 }
 
 void StrandContext::addWithPriority(
     Func func, Executor::KeepAlive<> executor, int8_t priority) {
-  addImpl(QueueItem{
-      std::move(func),
-      std::move(executor),
-      priority,
-      RequestContext::saveContext()});
+  addImpl(
+      QueueItem{
+          std::move(func),
+          std::move(executor),
+          priority,
+          RequestContext::saveContext()});
 }
 
 void StrandContext::addImpl(QueueItem&& item) {
diff --git a/folly/folly/executors/StripedEDFThreadPoolExecutor.cpp b/folly/folly/executors/StripedEDFThreadPoolExecutor.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/executors/StripedEDFThreadPoolExecutor.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/executors/StripedEDFThreadPoolExecutor.h>
+
+#include <optional>
+
+#include <glog/logging.h>
+#include <folly/concurrency/CacheLocality.h>
+#include <folly/container/IntrusiveHeap.h>
+#include <folly/synchronization/DistributedMutex.h>
+#include <folly/synchronization/StripedThrottledLifoSem.h>
+
+namespace folly {
+
+namespace {
+
+template <class T>
+class EDFPriorityQueue {
+ public:
+  EDFPriorityQueue() = default;
+
+  ~EDFPriorityQueue() {
+    while (!heap_.empty()) {
+      delete heap_.pop();
+    }
+  }
+
+  void enqueue(T&& item, uint64_t deadline) {
+    std::unique_ptr<Node> node{new Node{std::move(item), deadline}};
+    mutex_.lock_combine([&] { heap_.push(node.release()); });
+  }
+
+  T dequeue() {
+    std::unique_ptr<Node> node;
+    mutex_.lock_combine([&] { node.reset(heap_.pop()); });
+    CHECK(node);
+    return std::move(node->item);
+  }
+
+ private:
+  struct Node : IntrusiveHeapNode<> {
+    Node(T&& it, uint64_t d) : item(std::move(it)), deadline(d) {}
+
+    T item;
+    uint64_t deadline;
+
+    friend bool operator<(const Node& lhs, const Node& rhs) {
+      return lhs.deadline > rhs.deadline;
+    }
+  };
+
+  EDFPriorityQueue(const EDFPriorityQueue&) = delete;
+  EDFPriorityQueue(EDFPriorityQueue&&) = delete;
+  EDFPriorityQueue& operator=(const EDFPriorityQueue&) = delete;
+  EDFPriorityQueue& operator=(EDFPriorityQueue&&) = delete;
+
+  DistributedMutex mutex_;
+  IntrusiveHeap<Node> heap_;
+};
+
+template <class T>
+class BlockingQueueWithDeadline : public BlockingQueue<T> {
+ public:
+  virtual BlockingQueueAddResult addWithDeadline(
+      T&& item, uint64_t deadline) = 0;
+};
+
+template <class T>
+class StripedEDFPriorityBlockingQueue final
+    : public BlockingQueueWithDeadline<T> {
+ public:
+  explicit StripedEDFPriorityBlockingQueue(
+      const ThrottledLifoSem::Options& options)
+      : sem_(LLCAccessSpreader::get().numStripes(), std::tuple{}, options) {
+    StripedThrottledLifoSemBalancer::subscribe(sem_);
+  }
+
+  ~StripedEDFPriorityBlockingQueue() override {
+    StripedThrottledLifoSemBalancer::unsubscribe(sem_);
+  }
+
+  BlockingQueueAddResult addWithDeadline(T&& item, uint64_t deadline) override {
+    const auto stripeIdx = getStripeIdx();
+    sem_.payload(stripeIdx).enqueue(std::move(item), deadline);
+    return sem_.post(stripeIdx);
+  }
+
+  BlockingQueueAddResult add(T&& item) override {
+    return addWithDeadline(
+        std::move(item), StripedEDFThreadPoolExecutor::kLatestDeadline);
+  }
+
+  T take() override {
+    const auto stripeIdx = getStripeIdx();
+    auto foundStripeIdx = sem_.wait(stripeIdx);
+    return sem_.payload(foundStripeIdx).dequeue();
+  }
+
+  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {
+    const auto stripeIdx = getStripeIdx();
+    if (auto foundStripeIdx = sem_.try_wait_for(stripeIdx, time)) {
+      return sem_.payload(*foundStripeIdx).dequeue();
+    }
+    return none;
+  }
+
+  size_t size() override { return sem_.valueGuess(); }
+
+ private:
+  static size_t getStripeIdx() { return LLCAccessSpreader::get().current(); }
+
+  StripedThrottledLifoSem<EDFPriorityQueue<T>> sem_;
+};
+
+// Specialized implementation for the numStripes == 1 case, to avoid the
+// StripedThrottledLifoSem overhead when we don't need it.
+template <class T>
+class EDFPriorityBlockingQueue final : public BlockingQueueWithDeadline<T> {
+ public:
+  explicit EDFPriorityBlockingQueue(const ThrottledLifoSem::Options& options)
+      : sem_(options) {}
+
+  BlockingQueueAddResult addWithDeadline(T&& item, uint64_t deadline) override {
+    pq_.enqueue(std::move(item), deadline);
+    return sem_.post();
+  }
+
+  BlockingQueueAddResult add(T&& item) override {
+    return addWithDeadline(
+        std::move(item), StripedEDFThreadPoolExecutor::kLatestDeadline);
+  }
+
+  T take() override {
+    sem_.wait();
+    return pq_.dequeue();
+  }
+
+  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {
+    if (sem_.try_wait_for(time)) {
+      return pq_.dequeue();
+    }
+    return none;
+  }
+
+  size_t size() override { return sem_.valueGuess(); }
+
+ private:
+  EDFPriorityQueue<T> pq_;
+  ThrottledLifoSem sem_;
+};
+
+std::unique_ptr<BlockingQueue<CPUThreadPoolExecutor::CPUTask>> makeQueue(
+    const StripedEDFThreadPoolExecutor::Options& options) {
+  if (LLCAccessSpreader::get().numStripes() > 1) {
+    return std::make_unique<
+        StripedEDFPriorityBlockingQueue<CPUThreadPoolExecutor::CPUTask>>(
+        options.tlsOptions);
+  } else {
+    return std::make_unique<
+        EDFPriorityBlockingQueue<CPUThreadPoolExecutor::CPUTask>>(
+        options.tlsOptions);
+  }
+}
+
+} // namespace
+
+StripedEDFThreadPoolExecutor::StripedEDFThreadPoolExecutor(
+    std::pair<size_t, size_t> numThreads,
+    std::shared_ptr<ThreadFactory> threadFactory,
+    const Options& options)
+    : CPUThreadPoolExecutor(
+          numThreads, makeQueue(options), std::move(threadFactory)) {}
+
+void StripedEDFThreadPoolExecutor::add(Func f, uint64_t deadline) {
+  CPUTask task(std::move(f), {}, {}, 0);
+  CPUThreadPoolExecutor::addImpl(
+      [this, deadline](auto&& task) {
+        using QueueType = BlockingQueueWithDeadline<CPUTask>;
+        auto* taskQueue = static_cast<QueueType*>(getTaskQueue());
+        DCHECK_EQ(taskQueue, dynamic_cast<QueueType*>(getTaskQueue()));
+        return taskQueue->addWithDeadline(std::move(task), deadline);
+      },
+      std::move(task));
+}
+
+void StripedEDFThreadPoolExecutor::add(
+    std::vector<Func> fs, uint64_t deadline) {
+  // It might be possible to optimize this if we implement post(n) in
+  // StripedThrottledLifoSem, but this variant is very rarely used.
+  for (auto& f : fs) {
+    add(std::move(f), deadline);
+  }
+}
+
+} // namespace folly
diff --git a/folly/folly/executors/StripedEDFThreadPoolExecutor.h b/folly/folly/executors/StripedEDFThreadPoolExecutor.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/executors/StripedEDFThreadPoolExecutor.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/executors/CPUThreadPoolExecutor.h>
+#include <folly/executors/SoftRealTimeExecutor.h>
+#include <folly/synchronization/ThrottledLifoSem.h>
+
+namespace folly {
+
+/**
+ * An approximate implementation of an Earliest Deadline First executor.
+ *
+ * Instead of having a global priority queue, we maintain one independent queue
+ * for each LLC cache, to avoid expensive cross-LLC traffic. This implies that
+ * the EDF policy is only honored among tasks submitted from CPUs sharing the
+ * same LLC. In practice, each LLC should have enough CPUs to make the
+ * approximation good enough for most use cases.
+ */
+class StripedEDFThreadPoolExecutor
+    : public SoftRealTimeExecutor,
+      public CPUThreadPoolExecutor {
+ public:
+  struct Options {
+    ThrottledLifoSem::Options tlsOptions;
+  };
+
+  static constexpr uint64_t kEarliestDeadline = 0;
+  static constexpr uint64_t kLatestDeadline =
+      std::numeric_limits<uint64_t>::max();
+
+  explicit StripedEDFThreadPoolExecutor(
+      size_t numThreads,
+      std::shared_ptr<ThreadFactory> threadFactory =
+          std::make_shared<NamedThreadFactory>("StripedEDFTP"),
+      const Options& options = {})
+      : StripedEDFThreadPoolExecutor(
+            {numThreads, numThreads}, std::move(threadFactory), options) {}
+
+  explicit StripedEDFThreadPoolExecutor(
+      std::pair<size_t, size_t> numThreads,
+      std::shared_ptr<ThreadFactory> threadFactory =
+          std::make_shared<NamedThreadFactory>("StripedEDFTP"),
+      const Options& options = {});
+
+  using CPUThreadPoolExecutor::add;
+
+  // SoftRealTimeExecutor
+  void add(Func f, uint64_t deadline) override;
+  void add(std::vector<Func> fs, uint64_t deadline) override;
+};
+
+} // namespace folly
diff --git a/folly/folly/executors/ThreadPoolExecutor.cpp b/folly/folly/executors/ThreadPoolExecutor.cpp
--- a/folly/folly/executors/ThreadPoolExecutor.cpp
+++ b/folly/folly/executors/ThreadPoolExecutor.cpp
@@ -168,6 +168,8 @@
     observer.taskProcessed(taskInfo);
   });
 
+  thread->processedTasks = thread->processedTasks + 1;
+
   thread->idle.store(true, std::memory_order_relaxed);
   thread->lastActiveTime.store(
       std::chrono::steady_clock::now(), std::memory_order_relaxed);
@@ -217,6 +219,11 @@
       minthreads = numThreads;
       minThreads_.store(numThreads, std::memory_order_relaxed);
     }
+    // threadsCanTimeout_ can only transition from false to true if maxThreads_
+    // is increased (in which case minThreads_ will stay the same). So even if
+    // the current threads cannot timeout until the next wakeup, all the new
+    // threads can timeout, thus we can get down to minThreads_ active threads.
+    threadsCanTimeout_.store(minthreads != numThreads);
     if (active > numThreads) {
       numThreadsToJoin = active - numThreads;
       assert(numThreadsToJoin <= active - minthreads);
@@ -266,7 +273,8 @@
 
 // threadListLock_ is writelocked
 void ThreadPoolExecutor::removeThreads(size_t n, bool isJoin) {
-  isJoin_ = isJoin;
+  // Block early thread stopping.
+  isJoin_.store(isJoin, std::memory_order_release);
   stopThreads(n);
 }
 
@@ -317,6 +325,7 @@
   ThreadPoolExecutor::PoolStats stats;
   size_t activeTasks = 0;
   size_t idleAlive = 0;
+  uint64_t processedTasks = stoppedThreadProcessedTasks_;
   for (const auto& thread : threadList_.get()) {
     if (thread->idle.load(std::memory_order_relaxed)) {
       const std::chrono::nanoseconds idleTime =
@@ -326,9 +335,11 @@
     } else {
       activeTasks++;
     }
+    processedTasks += thread->processedTasks;
   }
   stats.pendingTaskCount = getPendingTaskCountImpl();
   stats.totalTaskCount = stats.pendingTaskCount + activeTasks;
+  stats.processedTaskCount = processedTasks;
 
   stats.threadCount = maxThreads_.load(std::memory_order_relaxed);
   stats.activeThreadCount =
@@ -395,7 +406,7 @@
 }
 
 BlockingQueueAddResult ThreadPoolExecutor::StoppedThreadQueue::add(
-    ThreadPoolExecutor::ThreadPtr item) {
+    ThreadPoolExecutor::ThreadPtr&& item) {
   std::lock_guard guard(mutex_);
   queue_.push(std::move(item));
   return sem_.post();
diff --git a/folly/folly/executors/ThreadPoolExecutor.h b/folly/folly/executors/ThreadPoolExecutor.h
--- a/folly/folly/executors/ThreadPoolExecutor.h
+++ b/folly/folly/executors/ThreadPoolExecutor.h
@@ -32,6 +32,7 @@
 #include <folly/portability/GFlags.h>
 #include <folly/synchronization/AtomicStruct.h>
 #include <folly/synchronization/Baton.h>
+#include <folly/synchronization/RelaxedAtomic.h>
 
 namespace folly {
 
@@ -122,9 +123,10 @@
           activeThreadCount(0),
           pendingTaskCount(0),
           totalTaskCount(0),
+          processedTaskCount(0),
           maxIdleTime(0) {}
     size_t threadCount, idleThreadCount, activeThreadCount;
-    uint64_t pendingTaskCount, totalTaskCount;
+    uint64_t pendingTaskCount, totalTaskCount, processedTaskCount;
     std::chrono::nanoseconds maxIdleTime;
   };
 
@@ -237,6 +239,11 @@
 
     static std::atomic<uint64_t> nextId;
     uint64_t id;
+
+    // Number of tasks processed by this worker.  Reset to zero when
+    // the thread stops.
+    folly::relaxed_atomic<uint64_t> processedTasks;
+
     std::thread handle;
     std::atomic<bool> idle;
     folly::AtomicStruct<std::chrono::steady_clock::time_point> lastActiveTime;
@@ -360,7 +367,7 @@
 
   class StoppedThreadQueue : public BlockingQueue<ThreadPtr> {
    public:
-    BlockingQueueAddResult add(ThreadPtr item) override;
+    BlockingQueueAddResult add(ThreadPtr&& item) override;
     ThreadPtr take() override;
     size_t size() override;
     folly::Optional<ThreadPtr> try_take_for(
@@ -394,9 +401,15 @@
   std::atomic<size_t> maxThreads_{0};
   std::atomic<size_t> minThreads_{0};
   std::atomic<size_t> activeThreads_{0};
+  std::atomic<bool> threadsCanTimeout_{true};
 
   std::atomic<size_t> threadsToJoin_{0};
   std::atomic<std::chrono::milliseconds> threadTimeout_;
+
+  // Number of tasks processed by stopped or joined threads.  Updated
+  // when a thread stops, which preceeds joining.  Requires holding
+  // the threadListLock_.
+  uint64_t stoppedThreadProcessedTasks_{0};
 
   bool joinKeepAliveOnce() {
     if (!std::exchange(keepAliveJoined_, true)) {
diff --git a/folly/folly/executors/task_queue/BlockingQueue.h b/folly/folly/executors/task_queue/BlockingQueue.h
--- a/folly/folly/executors/task_queue/BlockingQueue.h
+++ b/folly/folly/executors/task_queue/BlockingQueue.h
@@ -50,9 +50,9 @@
   // Returns true if an existing thread was able to work on it (used
   // for dynamically sizing thread pools), false otherwise.  Return false
   // if this feature is not supported.
-  virtual BlockingQueueAddResult add(T item) = 0;
+  virtual BlockingQueueAddResult add(T&& item) = 0;
   virtual BlockingQueueAddResult addWithPriority(
-      T item, int8_t /* priority */) {
+      T&& item, int8_t /* priority */) {
     return add(std::move(item));
   }
   virtual uint8_t getNumPriorities() { return 1; }
diff --git a/folly/folly/executors/task_queue/LifoSemMPMCQueue.h b/folly/folly/executors/task_queue/LifoSemMPMCQueue.h
--- a/folly/folly/executors/task_queue/LifoSemMPMCQueue.h
+++ b/folly/folly/executors/task_queue/LifoSemMPMCQueue.h
@@ -34,7 +34,7 @@
       const typename Semaphore::Options& semaphoreOptions = {})
       : sem_(semaphoreOptions), queue_(max_capacity) {}
 
-  BlockingQueueAddResult add(T item) override {
+  BlockingQueueAddResult add(T&& item) override {
     switch (kBehavior) { // static
       case QueueBehaviorIfFull::THROW:
         if (!queue_.writeIfNotFull(std::move(item))) {
@@ -49,26 +49,26 @@
   }
 
   T take() override {
+    sem_.wait();
     T item;
     while (!queue_.readIfNotEmpty(item)) {
-      sem_.wait();
     }
     return item;
   }
 
   folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {
+    if (!sem_.try_wait_for(time)) {
+      return folly::none;
+    }
     T item;
     while (!queue_.readIfNotEmpty(item)) {
-      if (!sem_.try_wait_for(time)) {
-        return folly::none;
-      }
     }
     return item;
   }
 
   size_t capacity() { return queue_.capacity(); }
 
-  size_t size() override { return queue_.size(); }
+  size_t size() override { return sem_.valueGuess(); }
 
  private:
   Semaphore sem_;
diff --git a/folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h b/folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h
--- a/folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h
+++ b/folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h
@@ -63,11 +63,11 @@
   uint8_t getNumPriorities() override { return queues_.size(); }
 
   // Add at medium priority by default
-  BlockingQueueAddResult add(T item) override {
+  BlockingQueueAddResult add(T&& item) override {
     return addWithPriority(std::move(item), folly::Executor::MID_PRI);
   }
 
-  BlockingQueueAddResult addWithPriority(T item, int8_t priority) override {
+  BlockingQueueAddResult addWithPriority(T&& item, int8_t priority) override {
     int mid = getNumPriorities() / 2;
     size_t queue = priority < 0
         ? std::max(0, mid + priority)
@@ -87,24 +87,24 @@
   }
 
   T take() override {
+    sem_.wait();
     T item;
     while (true) {
       if (nonBlockingTake(item)) {
         return item;
       }
-      sem_.wait();
     }
   }
 
   folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {
+    if (!sem_.try_wait_for(time)) {
+      return folly::none;
+    }
     T item;
     while (true) {
       if (nonBlockingTake(item)) {
         return item;
       }
-      if (!sem_.try_wait_for(time)) {
-        return folly::none;
-      }
     }
   }
 
@@ -117,21 +117,7 @@
     return false;
   }
 
-  size_t size() override {
-    size_t size = 0;
-    for (auto& q : queues_) {
-      size += q.size();
-    }
-    return size;
-  }
-
-  size_t sizeGuess() const {
-    size_t size = 0;
-    for (auto& q : queues_) {
-      size += q.sizeGuess();
-    }
-    return size;
-  }
+  size_t size() override { return sem_.valueGuess(); }
 
  private:
   Semaphore sem_;
diff --git a/folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h b/folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h
--- a/folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h
+++ b/folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h
@@ -38,11 +38,11 @@
   uint8_t getNumPriorities() override { return queue_.priorities(); }
 
   // Add at medium priority by default
-  BlockingQueueAddResult add(T item) override {
+  BlockingQueueAddResult add(T&& item) override {
     return addWithPriority(std::move(item), folly::Executor::MID_PRI);
   }
 
-  BlockingQueueAddResult addWithPriority(T item, int8_t priority) override {
+  BlockingQueueAddResult addWithPriority(T&& item, int8_t priority) override {
     queue_.at_priority(translatePriority(priority)).enqueue(std::move(item));
     return sem_.post();
   }
@@ -66,9 +66,7 @@
     return dequeue();
   }
 
-  size_t size() override { return queue_.size(); }
-
-  size_t sizeGuess() const { return queue_.size(); }
+  size_t size() override { return sem_.valueGuess(); }
 
  private:
   size_t translatePriority(int8_t const priority) {
diff --git a/folly/folly/executors/task_queue/StripedPriorityUnboundedBlockingQueue.h b/folly/folly/executors/task_queue/StripedPriorityUnboundedBlockingQueue.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/executors/task_queue/StripedPriorityUnboundedBlockingQueue.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/ConstexprMath.h>
+#include <folly/Executor.h>
+#include <folly/GLog.h>
+#include <folly/concurrency/CacheLocality.h>
+#include <folly/concurrency/PriorityUnboundedQueueSet.h>
+#include <folly/executors/task_queue/BlockingQueue.h>
+#include <folly/executors/task_queue/PriorityUnboundedBlockingQueue.h>
+#include <folly/lang/Exception.h>
+#include <folly/synchronization/StripedThrottledLifoSem.h>
+
+namespace folly {
+
+/**
+ * A BlockingQueue sharded by LLC cache. Compared to the default
+ * PriorityUnboundedBlockingQueue, this can reduce contention on systems with a
+ * large number of LLC caches, at the cost of unfairness and work conservation;
+ * see the StripedThrottledLifoSem documentation for a detailed explanation.
+ *
+ * This queue should preferably be used on thread pools with numThreads equal to
+ * the number of available cores, in order to guarantee good balancing across
+ * the LLCs. For the same reason, dynamic resizing should be disabled.
+ *
+ * NOTE: StripeGetter is customizable only for testing purposes.
+ */
+template <typename T, class StripeGetter = LLCAccessSpreader>
+class StripedPriorityUnboundedBlockingQueue : public BlockingQueue<T> {
+  struct PrivateTag {};
+
+ public:
+  static std::unique_ptr<BlockingQueue<T>> create(
+      uint8_t numPriorities,
+      const typename ThrottledLifoSem::Options& semaphoreOptions = {}) {
+    const auto numStripes = StripeGetter::get().numStripes();
+    if (numStripes == 1) {
+      // Don't need the overhead, just use the non-striped version.
+      return std::make_unique<
+          PriorityUnboundedBlockingQueue<T, folly::ThrottledLifoSem>>(
+          numPriorities, semaphoreOptions);
+    }
+    return std::make_unique<
+        StripedPriorityUnboundedBlockingQueue<T, StripeGetter>>(
+        PrivateTag{}, numStripes, numPriorities, semaphoreOptions);
+  }
+
+  // Private, use create().
+  StripedPriorityUnboundedBlockingQueue(
+      PrivateTag,
+      size_t numStripes,
+      uint8_t numPriorities,
+      const typename ThrottledLifoSem::Options& semaphoreOptions = {})
+      : numPriorities_(numPriorities),
+        sem_(numStripes, std::tuple{numPriorities_}, semaphoreOptions) {
+    StripedThrottledLifoSemBalancer::subscribe(sem_);
+  }
+
+  ~StripedPriorityUnboundedBlockingQueue() override {
+    StripedThrottledLifoSemBalancer::unsubscribe(sem_);
+  }
+
+  uint8_t getNumPriorities() override { return numPriorities_; }
+
+  BlockingQueueAddResult add(T&& item) override {
+    return addWithPriority(std::move(item), folly::Executor::MID_PRI);
+  }
+
+  BlockingQueueAddResult addWithPriority(T&& item, int8_t priority) override {
+    auto stripeIdx = getStripeIdx();
+    sem_.payload(stripeIdx)
+        .at_priority(translatePriority(priority))
+        .enqueue(std::move(item));
+    return sem_.post(stripeIdx);
+  }
+
+  T take() override {
+    const auto stripeIdx = getStripeIdx();
+    auto foundStripeIdx = sem_.wait(stripeIdx);
+    return dequeue(foundStripeIdx);
+  }
+
+  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {
+    const auto stripeIdx = getStripeIdx();
+    if (auto foundStripeIdx = sem_.try_wait_for(stripeIdx, time)) {
+      return dequeue(*foundStripeIdx);
+    }
+    return none;
+  }
+
+  size_t size() override { return sem_.valueGuess(); }
+
+ private:
+  size_t translatePriority(int8_t const priority) {
+    int8_t const hi = (numPriorities_ + 1) / 2 - 1;
+    int8_t const lo = hi - (numPriorities_ - 1);
+    return hi - constexpr_clamp(priority, lo, hi);
+  }
+
+  T dequeue(size_t stripeIdx) {
+    // must follow a successful sem wait
+    if (auto obj = sem_.payload(stripeIdx).try_dequeue()) {
+      return std::move(*obj);
+    }
+    terminate_with<std::logic_error>("bug in task queue");
+  }
+
+  static size_t getStripeIdx() { return StripeGetter::get().current(); }
+
+  using Queue = PriorityUMPMCQueueSet<T, /* MayBlock = */ false>;
+
+  const size_t numPriorities_;
+  StripedThrottledLifoSem<Queue> sem_;
+};
+
+} // namespace folly
diff --git a/folly/folly/executors/task_queue/UnboundedBlockingQueue.h b/folly/folly/executors/task_queue/UnboundedBlockingQueue.h
--- a/folly/folly/executors/task_queue/UnboundedBlockingQueue.h
+++ b/folly/folly/executors/task_queue/UnboundedBlockingQueue.h
@@ -29,7 +29,7 @@
       const typename Semaphore::Options& semaphoreOptions = {})
       : sem_(semaphoreOptions) {}
 
-  BlockingQueueAddResult add(T item) override {
+  BlockingQueueAddResult add(T&& item) override {
     queue_.enqueue(std::move(item));
     return sem_.post();
   }
@@ -46,7 +46,7 @@
     return queue_.dequeue();
   }
 
-  size_t size() override { return queue_.size(); }
+  size_t size() override { return sem_.valueGuess(); }
 
  private:
   Semaphore sem_;
diff --git a/folly/folly/executors/thread_factory/InitThreadFactory.h b/folly/folly/executors/thread_factory/InitThreadFactory.h
--- a/folly/folly/executors/thread_factory/InitThreadFactory.h
+++ b/folly/folly/executors/thread_factory/InitThreadFactory.h
@@ -31,8 +31,9 @@
       Func&& threadInitializer,
       Func&& threadFinializer = [] {})
       : threadFactory_(std::move(threadFactory)),
-        threadInitFini_(std::make_shared<ThreadInitFini>(
-            std::move(threadInitializer), std::move(threadFinializer))) {}
+        threadInitFini_(
+            std::make_shared<ThreadInitFini>(
+                std::move(threadInitializer), std::move(threadFinializer))) {}
 
   std::thread newThread(Func&& func) override {
     return threadFactory_->newThread(
diff --git a/folly/folly/experimental/EventCount.h b/folly/folly/experimental/EventCount.h
--- a/folly/folly/experimental/EventCount.h
+++ b/folly/folly/experimental/EventCount.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/synchronization/EventCount.h>
+#include <folly/synchronization/EventCount.h> // @shim
diff --git a/folly/folly/experimental/FlatCombiningPriorityQueue.h b/folly/folly/experimental/FlatCombiningPriorityQueue.h
--- a/folly/folly/experimental/FlatCombiningPriorityQueue.h
+++ b/folly/folly/experimental/FlatCombiningPriorityQueue.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/concurrency/container/FlatCombiningPriorityQueue.h>
+#include <folly/concurrency/container/FlatCombiningPriorityQueue.h> // @shim
diff --git a/folly/folly/experimental/FunctionScheduler.h b/folly/folly/experimental/FunctionScheduler.h
--- a/folly/folly/experimental/FunctionScheduler.h
+++ b/folly/folly/experimental/FunctionScheduler.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/executors/FunctionScheduler.h>
+#include <folly/executors/FunctionScheduler.h> // @shim
diff --git a/folly/folly/experimental/TestUtil.h b/folly/folly/experimental/TestUtil.h
deleted file mode 100644
--- a/folly/folly/experimental/TestUtil.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/testing/TestUtil.h>
diff --git a/folly/folly/experimental/ThreadedRepeatingFunctionRunner.h b/folly/folly/experimental/ThreadedRepeatingFunctionRunner.h
--- a/folly/folly/experimental/ThreadedRepeatingFunctionRunner.h
+++ b/folly/folly/experimental/ThreadedRepeatingFunctionRunner.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/executors/ThreadedRepeatingFunctionRunner.h>
+#include <folly/executors/ThreadedRepeatingFunctionRunner.h> // @shim
diff --git a/folly/folly/experimental/channels/Channel-fwd.h b/folly/folly/experimental/channels/Channel-fwd.h
--- a/folly/folly/experimental/channels/Channel-fwd.h
+++ b/folly/folly/experimental/channels/Channel-fwd.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Channel-fwd.h>
+#include <folly/channels/Channel-fwd.h> // @shim
diff --git a/folly/folly/experimental/channels/Channel-inl.h b/folly/folly/experimental/channels/Channel-inl.h
--- a/folly/folly/experimental/channels/Channel-inl.h
+++ b/folly/folly/experimental/channels/Channel-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Channel-inl.h>
+#include <folly/channels/Channel-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/Channel.h b/folly/folly/experimental/channels/Channel.h
--- a/folly/folly/experimental/channels/Channel.h
+++ b/folly/folly/experimental/channels/Channel.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Channel.h>
+#include <folly/channels/Channel.h> // @shim
diff --git a/folly/folly/experimental/channels/ChannelCallbackHandle.h b/folly/folly/experimental/channels/ChannelCallbackHandle.h
--- a/folly/folly/experimental/channels/ChannelCallbackHandle.h
+++ b/folly/folly/experimental/channels/ChannelCallbackHandle.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/ChannelCallbackHandle.h>
+#include <folly/channels/ChannelCallbackHandle.h> // @shim
diff --git a/folly/folly/experimental/channels/ChannelProcessor-inl.h b/folly/folly/experimental/channels/ChannelProcessor-inl.h
--- a/folly/folly/experimental/channels/ChannelProcessor-inl.h
+++ b/folly/folly/experimental/channels/ChannelProcessor-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/ChannelProcessor-inl.h>
+#include <folly/channels/ChannelProcessor-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/ChannelProcessor.h b/folly/folly/experimental/channels/ChannelProcessor.h
--- a/folly/folly/experimental/channels/ChannelProcessor.h
+++ b/folly/folly/experimental/channels/ChannelProcessor.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/ChannelProcessor.h>
+#include <folly/channels/ChannelProcessor.h> // @shim
diff --git a/folly/folly/experimental/channels/ConsumeChannel-inl.h b/folly/folly/experimental/channels/ConsumeChannel-inl.h
--- a/folly/folly/experimental/channels/ConsumeChannel-inl.h
+++ b/folly/folly/experimental/channels/ConsumeChannel-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/ConsumeChannel-inl.h>
+#include <folly/channels/ConsumeChannel-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/ConsumeChannel.h b/folly/folly/experimental/channels/ConsumeChannel.h
--- a/folly/folly/experimental/channels/ConsumeChannel.h
+++ b/folly/folly/experimental/channels/ConsumeChannel.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/ConsumeChannel.h>
+#include <folly/channels/ConsumeChannel.h> // @shim
diff --git a/folly/folly/experimental/channels/FanoutChannel-inl.h b/folly/folly/experimental/channels/FanoutChannel-inl.h
--- a/folly/folly/experimental/channels/FanoutChannel-inl.h
+++ b/folly/folly/experimental/channels/FanoutChannel-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/FanoutChannel-inl.h>
+#include <folly/channels/FanoutChannel-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/FanoutChannel.h b/folly/folly/experimental/channels/FanoutChannel.h
--- a/folly/folly/experimental/channels/FanoutChannel.h
+++ b/folly/folly/experimental/channels/FanoutChannel.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/FanoutChannel.h>
+#include <folly/channels/FanoutChannel.h> // @shim
diff --git a/folly/folly/experimental/channels/FanoutSender-inl.h b/folly/folly/experimental/channels/FanoutSender-inl.h
--- a/folly/folly/experimental/channels/FanoutSender-inl.h
+++ b/folly/folly/experimental/channels/FanoutSender-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/FanoutSender-inl.h>
+#include <folly/channels/FanoutSender-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/FanoutSender.h b/folly/folly/experimental/channels/FanoutSender.h
--- a/folly/folly/experimental/channels/FanoutSender.h
+++ b/folly/folly/experimental/channels/FanoutSender.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/FanoutSender.h>
+#include <folly/channels/FanoutSender.h> // @shim
diff --git a/folly/folly/experimental/channels/MaxConcurrentRateLimiter.h b/folly/folly/experimental/channels/MaxConcurrentRateLimiter.h
--- a/folly/folly/experimental/channels/MaxConcurrentRateLimiter.h
+++ b/folly/folly/experimental/channels/MaxConcurrentRateLimiter.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/MaxConcurrentRateLimiter.h>
+#include <folly/channels/MaxConcurrentRateLimiter.h> // @shim
diff --git a/folly/folly/experimental/channels/Merge-inl.h b/folly/folly/experimental/channels/Merge-inl.h
--- a/folly/folly/experimental/channels/Merge-inl.h
+++ b/folly/folly/experimental/channels/Merge-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Merge-inl.h>
+#include <folly/channels/Merge-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/Merge.h b/folly/folly/experimental/channels/Merge.h
--- a/folly/folly/experimental/channels/Merge.h
+++ b/folly/folly/experimental/channels/Merge.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Merge.h>
+#include <folly/channels/Merge.h> // @shim
diff --git a/folly/folly/experimental/channels/MergeChannel-inl.h b/folly/folly/experimental/channels/MergeChannel-inl.h
--- a/folly/folly/experimental/channels/MergeChannel-inl.h
+++ b/folly/folly/experimental/channels/MergeChannel-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/MergeChannel-inl.h>
+#include <folly/channels/MergeChannel-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/MergeChannel.h b/folly/folly/experimental/channels/MergeChannel.h
--- a/folly/folly/experimental/channels/MergeChannel.h
+++ b/folly/folly/experimental/channels/MergeChannel.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/MergeChannel.h>
+#include <folly/channels/MergeChannel.h> // @shim
diff --git a/folly/folly/experimental/channels/MultiplexChannel-inl.h b/folly/folly/experimental/channels/MultiplexChannel-inl.h
--- a/folly/folly/experimental/channels/MultiplexChannel-inl.h
+++ b/folly/folly/experimental/channels/MultiplexChannel-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/MultiplexChannel-inl.h>
+#include <folly/channels/MultiplexChannel-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/MultiplexChannel.h b/folly/folly/experimental/channels/MultiplexChannel.h
--- a/folly/folly/experimental/channels/MultiplexChannel.h
+++ b/folly/folly/experimental/channels/MultiplexChannel.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/MultiplexChannel.h>
+#include <folly/channels/MultiplexChannel.h> // @shim
diff --git a/folly/folly/experimental/channels/OnClosedException.h b/folly/folly/experimental/channels/OnClosedException.h
--- a/folly/folly/experimental/channels/OnClosedException.h
+++ b/folly/folly/experimental/channels/OnClosedException.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/OnClosedException.h>
+#include <folly/channels/OnClosedException.h> // @shim
diff --git a/folly/folly/experimental/channels/Producer-inl.h b/folly/folly/experimental/channels/Producer-inl.h
--- a/folly/folly/experimental/channels/Producer-inl.h
+++ b/folly/folly/experimental/channels/Producer-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Producer-inl.h>
+#include <folly/channels/Producer-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/Producer.h b/folly/folly/experimental/channels/Producer.h
--- a/folly/folly/experimental/channels/Producer.h
+++ b/folly/folly/experimental/channels/Producer.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Producer.h>
+#include <folly/channels/Producer.h> // @shim
diff --git a/folly/folly/experimental/channels/ProxyChannel-inl.h b/folly/folly/experimental/channels/ProxyChannel-inl.h
--- a/folly/folly/experimental/channels/ProxyChannel-inl.h
+++ b/folly/folly/experimental/channels/ProxyChannel-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/ProxyChannel-inl.h>
+#include <folly/channels/ProxyChannel-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/ProxyChannel.h b/folly/folly/experimental/channels/ProxyChannel.h
--- a/folly/folly/experimental/channels/ProxyChannel.h
+++ b/folly/folly/experimental/channels/ProxyChannel.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/ProxyChannel.h>
+#include <folly/channels/ProxyChannel.h> // @shim
diff --git a/folly/folly/experimental/channels/RateLimiter.h b/folly/folly/experimental/channels/RateLimiter.h
--- a/folly/folly/experimental/channels/RateLimiter.h
+++ b/folly/folly/experimental/channels/RateLimiter.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/RateLimiter.h>
+#include <folly/channels/RateLimiter.h> // @shim
diff --git a/folly/folly/experimental/channels/Transform-inl.h b/folly/folly/experimental/channels/Transform-inl.h
--- a/folly/folly/experimental/channels/Transform-inl.h
+++ b/folly/folly/experimental/channels/Transform-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Transform-inl.h>
+#include <folly/channels/Transform-inl.h> // @shim
diff --git a/folly/folly/experimental/channels/Transform.h b/folly/folly/experimental/channels/Transform.h
--- a/folly/folly/experimental/channels/Transform.h
+++ b/folly/folly/experimental/channels/Transform.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/Transform.h>
+#include <folly/channels/Transform.h> // @shim
diff --git a/folly/folly/experimental/channels/detail/AtomicQueue.h b/folly/folly/experimental/channels/detail/AtomicQueue.h
--- a/folly/folly/experimental/channels/detail/AtomicQueue.h
+++ b/folly/folly/experimental/channels/detail/AtomicQueue.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/detail/AtomicQueue.h>
+#include <folly/channels/detail/AtomicQueue.h> // @shim
diff --git a/folly/folly/experimental/channels/detail/ChannelBridge.h b/folly/folly/experimental/channels/detail/ChannelBridge.h
--- a/folly/folly/experimental/channels/detail/ChannelBridge.h
+++ b/folly/folly/experimental/channels/detail/ChannelBridge.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/detail/ChannelBridge.h>
+#include <folly/channels/detail/ChannelBridge.h> // @shim
diff --git a/folly/folly/experimental/channels/detail/FunctionTraits.h b/folly/folly/experimental/channels/detail/FunctionTraits.h
--- a/folly/folly/experimental/channels/detail/FunctionTraits.h
+++ b/folly/folly/experimental/channels/detail/FunctionTraits.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/detail/FunctionTraits.h>
+#include <folly/channels/detail/FunctionTraits.h> // @shim
diff --git a/folly/folly/experimental/channels/detail/IntrusivePtr.h b/folly/folly/experimental/channels/detail/IntrusivePtr.h
--- a/folly/folly/experimental/channels/detail/IntrusivePtr.h
+++ b/folly/folly/experimental/channels/detail/IntrusivePtr.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/detail/IntrusivePtr.h>
+#include <folly/channels/detail/IntrusivePtr.h> // @shim
diff --git a/folly/folly/experimental/channels/detail/MultiplexerTraits.h b/folly/folly/experimental/channels/detail/MultiplexerTraits.h
--- a/folly/folly/experimental/channels/detail/MultiplexerTraits.h
+++ b/folly/folly/experimental/channels/detail/MultiplexerTraits.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/detail/MultiplexerTraits.h>
+#include <folly/channels/detail/MultiplexerTraits.h> // @shim
diff --git a/folly/folly/experimental/channels/detail/PointerVariant.h b/folly/folly/experimental/channels/detail/PointerVariant.h
--- a/folly/folly/experimental/channels/detail/PointerVariant.h
+++ b/folly/folly/experimental/channels/detail/PointerVariant.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/detail/PointerVariant.h>
+#include <folly/channels/detail/PointerVariant.h> // @shim
diff --git a/folly/folly/experimental/channels/detail/Utility.h b/folly/folly/experimental/channels/detail/Utility.h
--- a/folly/folly/experimental/channels/detail/Utility.h
+++ b/folly/folly/experimental/channels/detail/Utility.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/channels/detail/Utility.h>
+#include <folly/channels/detail/Utility.h> // @shim
diff --git a/folly/folly/experimental/coro/AsyncGenerator.h b/folly/folly/experimental/coro/AsyncGenerator.h
--- a/folly/folly/experimental/coro/AsyncGenerator.h
+++ b/folly/folly/experimental/coro/AsyncGenerator.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/AsyncGenerator.h>
+#include <folly/coro/AsyncGenerator.h> // @shim
diff --git a/folly/folly/experimental/coro/AsyncPipe.h b/folly/folly/experimental/coro/AsyncPipe.h
--- a/folly/folly/experimental/coro/AsyncPipe.h
+++ b/folly/folly/experimental/coro/AsyncPipe.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/AsyncPipe.h>
+#include <folly/coro/AsyncPipe.h> // @shim
diff --git a/folly/folly/experimental/coro/AsyncScope.h b/folly/folly/experimental/coro/AsyncScope.h
--- a/folly/folly/experimental/coro/AsyncScope.h
+++ b/folly/folly/experimental/coro/AsyncScope.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/AsyncScope.h>
+#include <folly/coro/AsyncScope.h> // @shim
diff --git a/folly/folly/experimental/coro/AsyncStack.h b/folly/folly/experimental/coro/AsyncStack.h
--- a/folly/folly/experimental/coro/AsyncStack.h
+++ b/folly/folly/experimental/coro/AsyncStack.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/AsyncStack.h>
+#include <folly/coro/AsyncStack.h> // @shim
diff --git a/folly/folly/experimental/coro/AutoCleanup-fwd.h b/folly/folly/experimental/coro/AutoCleanup-fwd.h
--- a/folly/folly/experimental/coro/AutoCleanup-fwd.h
+++ b/folly/folly/experimental/coro/AutoCleanup-fwd.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/AutoCleanup-fwd.h>
+#include <folly/coro/AutoCleanup-fwd.h> // @shim
diff --git a/folly/folly/experimental/coro/AutoCleanup.h b/folly/folly/experimental/coro/AutoCleanup.h
--- a/folly/folly/experimental/coro/AutoCleanup.h
+++ b/folly/folly/experimental/coro/AutoCleanup.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/AutoCleanup.h>
+#include <folly/coro/AutoCleanup.h> // @shim
diff --git a/folly/folly/experimental/coro/Baton.h b/folly/folly/experimental/coro/Baton.h
--- a/folly/folly/experimental/coro/Baton.h
+++ b/folly/folly/experimental/coro/Baton.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Baton.h>
+#include <folly/coro/Baton.h> // @shim
diff --git a/folly/folly/experimental/coro/BlockingWait.h b/folly/folly/experimental/coro/BlockingWait.h
--- a/folly/folly/experimental/coro/BlockingWait.h
+++ b/folly/folly/experimental/coro/BlockingWait.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/BlockingWait.h>
+#include <folly/coro/BlockingWait.h> // @shim
diff --git a/folly/folly/experimental/coro/BoundedQueue.h b/folly/folly/experimental/coro/BoundedQueue.h
--- a/folly/folly/experimental/coro/BoundedQueue.h
+++ b/folly/folly/experimental/coro/BoundedQueue.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/BoundedQueue.h>
+#include <folly/coro/BoundedQueue.h> // @shim
diff --git a/folly/folly/experimental/coro/Cleanup.h b/folly/folly/experimental/coro/Cleanup.h
--- a/folly/folly/experimental/coro/Cleanup.h
+++ b/folly/folly/experimental/coro/Cleanup.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Cleanup.h>
+#include <folly/coro/Cleanup.h> // @shim
diff --git a/folly/folly/experimental/coro/Collect-inl.h b/folly/folly/experimental/coro/Collect-inl.h
--- a/folly/folly/experimental/coro/Collect-inl.h
+++ b/folly/folly/experimental/coro/Collect-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Collect-inl.h>
+#include <folly/coro/Collect-inl.h> // @shim
diff --git a/folly/folly/experimental/coro/Collect.h b/folly/folly/experimental/coro/Collect.h
--- a/folly/folly/experimental/coro/Collect.h
+++ b/folly/folly/experimental/coro/Collect.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Collect.h>
+#include <folly/coro/Collect.h> // @shim
diff --git a/folly/folly/experimental/coro/Concat-inl.h b/folly/folly/experimental/coro/Concat-inl.h
--- a/folly/folly/experimental/coro/Concat-inl.h
+++ b/folly/folly/experimental/coro/Concat-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Concat-inl.h>
+#include <folly/coro/Concat-inl.h> // @shim
diff --git a/folly/folly/experimental/coro/Concat.h b/folly/folly/experimental/coro/Concat.h
--- a/folly/folly/experimental/coro/Concat.h
+++ b/folly/folly/experimental/coro/Concat.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Concat.h>
+#include <folly/coro/Concat.h> // @shim
diff --git a/folly/folly/experimental/coro/Coroutine.h b/folly/folly/experimental/coro/Coroutine.h
--- a/folly/folly/experimental/coro/Coroutine.h
+++ b/folly/folly/experimental/coro/Coroutine.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Coroutine.h>
+#include <folly/coro/Coroutine.h> // @shim
diff --git a/folly/folly/experimental/coro/CurrentExecutor.h b/folly/folly/experimental/coro/CurrentExecutor.h
--- a/folly/folly/experimental/coro/CurrentExecutor.h
+++ b/folly/folly/experimental/coro/CurrentExecutor.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/CurrentExecutor.h>
+#include <folly/coro/CurrentExecutor.h> // @shim
diff --git a/folly/folly/experimental/coro/DetachOnCancel.h b/folly/folly/experimental/coro/DetachOnCancel.h
--- a/folly/folly/experimental/coro/DetachOnCancel.h
+++ b/folly/folly/experimental/coro/DetachOnCancel.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/DetachOnCancel.h>
+#include <folly/coro/DetachOnCancel.h> // @shim
diff --git a/folly/folly/experimental/coro/Filter-inl.h b/folly/folly/experimental/coro/Filter-inl.h
--- a/folly/folly/experimental/coro/Filter-inl.h
+++ b/folly/folly/experimental/coro/Filter-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Filter-inl.h>
+#include <folly/coro/Filter-inl.h> // @shim
diff --git a/folly/folly/experimental/coro/Filter.h b/folly/folly/experimental/coro/Filter.h
--- a/folly/folly/experimental/coro/Filter.h
+++ b/folly/folly/experimental/coro/Filter.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Filter.h>
+#include <folly/coro/Filter.h> // @shim
diff --git a/folly/folly/experimental/coro/FutureUtil.h b/folly/folly/experimental/coro/FutureUtil.h
--- a/folly/folly/experimental/coro/FutureUtil.h
+++ b/folly/folly/experimental/coro/FutureUtil.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/FutureUtil.h>
+#include <folly/coro/FutureUtil.h> // @shim
diff --git a/folly/folly/experimental/coro/Generator.h b/folly/folly/experimental/coro/Generator.h
--- a/folly/folly/experimental/coro/Generator.h
+++ b/folly/folly/experimental/coro/Generator.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Generator.h>
+#include <folly/coro/Generator.h> // @shim
diff --git a/folly/folly/experimental/coro/GmockHelpers.h b/folly/folly/experimental/coro/GmockHelpers.h
--- a/folly/folly/experimental/coro/GmockHelpers.h
+++ b/folly/folly/experimental/coro/GmockHelpers.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/GmockHelpers.h>
+#include <folly/coro/GmockHelpers.h> // @shim
diff --git a/folly/folly/experimental/coro/GtestHelpers.h b/folly/folly/experimental/coro/GtestHelpers.h
--- a/folly/folly/experimental/coro/GtestHelpers.h
+++ b/folly/folly/experimental/coro/GtestHelpers.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/GtestHelpers.h>
+#include <folly/coro/GtestHelpers.h> // @shim
diff --git a/folly/folly/experimental/coro/Invoke.h b/folly/folly/experimental/coro/Invoke.h
--- a/folly/folly/experimental/coro/Invoke.h
+++ b/folly/folly/experimental/coro/Invoke.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Invoke.h>
+#include <folly/coro/Invoke.h> // @shim
diff --git a/folly/folly/experimental/coro/Merge-inl.h b/folly/folly/experimental/coro/Merge-inl.h
--- a/folly/folly/experimental/coro/Merge-inl.h
+++ b/folly/folly/experimental/coro/Merge-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Merge-inl.h>
+#include <folly/coro/Merge-inl.h> // @shim
diff --git a/folly/folly/experimental/coro/Merge.h b/folly/folly/experimental/coro/Merge.h
--- a/folly/folly/experimental/coro/Merge.h
+++ b/folly/folly/experimental/coro/Merge.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Merge.h>
+#include <folly/coro/Merge.h> // @shim
diff --git a/folly/folly/experimental/coro/Mutex.h b/folly/folly/experimental/coro/Mutex.h
--- a/folly/folly/experimental/coro/Mutex.h
+++ b/folly/folly/experimental/coro/Mutex.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Mutex.h>
+#include <folly/coro/Mutex.h> // @shim
diff --git a/folly/folly/experimental/coro/Promise.h b/folly/folly/experimental/coro/Promise.h
--- a/folly/folly/experimental/coro/Promise.h
+++ b/folly/folly/experimental/coro/Promise.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Promise.h>
+#include <folly/coro/Promise.h> // @shim
diff --git a/folly/folly/experimental/coro/Result.h b/folly/folly/experimental/coro/Result.h
--- a/folly/folly/experimental/coro/Result.h
+++ b/folly/folly/experimental/coro/Result.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Result.h>
+#include <folly/coro/Result.h> // @shim
diff --git a/folly/folly/experimental/coro/Retry.h b/folly/folly/experimental/coro/Retry.h
--- a/folly/folly/experimental/coro/Retry.h
+++ b/folly/folly/experimental/coro/Retry.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Retry.h>
+#include <folly/coro/Retry.h> // @shim
diff --git a/folly/folly/experimental/coro/RustAdaptors.h b/folly/folly/experimental/coro/RustAdaptors.h
--- a/folly/folly/experimental/coro/RustAdaptors.h
+++ b/folly/folly/experimental/coro/RustAdaptors.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/RustAdaptors.h>
+#include <folly/coro/RustAdaptors.h> // @shim
diff --git a/folly/folly/experimental/coro/ScopeExit.h b/folly/folly/experimental/coro/ScopeExit.h
--- a/folly/folly/experimental/coro/ScopeExit.h
+++ b/folly/folly/experimental/coro/ScopeExit.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/ScopeExit.h>
+#include <folly/coro/ScopeExit.h> // @shim
diff --git a/folly/folly/experimental/coro/SharedLock.h b/folly/folly/experimental/coro/SharedLock.h
--- a/folly/folly/experimental/coro/SharedLock.h
+++ b/folly/folly/experimental/coro/SharedLock.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/SharedLock.h>
+#include <folly/coro/SharedLock.h> // @shim
diff --git a/folly/folly/experimental/coro/SharedMutex.h b/folly/folly/experimental/coro/SharedMutex.h
--- a/folly/folly/experimental/coro/SharedMutex.h
+++ b/folly/folly/experimental/coro/SharedMutex.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/SharedMutex.h>
+#include <folly/coro/SharedMutex.h> // @shim
diff --git a/folly/folly/experimental/coro/SharedPromise.h b/folly/folly/experimental/coro/SharedPromise.h
--- a/folly/folly/experimental/coro/SharedPromise.h
+++ b/folly/folly/experimental/coro/SharedPromise.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/SharedPromise.h>
+#include <folly/coro/SharedPromise.h> // @shim
diff --git a/folly/folly/experimental/coro/Sleep-inl.h b/folly/folly/experimental/coro/Sleep-inl.h
--- a/folly/folly/experimental/coro/Sleep-inl.h
+++ b/folly/folly/experimental/coro/Sleep-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Sleep-inl.h>
+#include <folly/coro/Sleep-inl.h> // @shim
diff --git a/folly/folly/experimental/coro/Sleep.h b/folly/folly/experimental/coro/Sleep.h
--- a/folly/folly/experimental/coro/Sleep.h
+++ b/folly/folly/experimental/coro/Sleep.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Sleep.h>
+#include <folly/coro/Sleep.h> // @shim
diff --git a/folly/folly/experimental/coro/SmallUnboundedQueue.h b/folly/folly/experimental/coro/SmallUnboundedQueue.h
--- a/folly/folly/experimental/coro/SmallUnboundedQueue.h
+++ b/folly/folly/experimental/coro/SmallUnboundedQueue.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/SmallUnboundedQueue.h>
+#include <folly/coro/SmallUnboundedQueue.h> // @shim
diff --git a/folly/folly/experimental/coro/Task.h b/folly/folly/experimental/coro/Task.h
--- a/folly/folly/experimental/coro/Task.h
+++ b/folly/folly/experimental/coro/Task.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Task.h>
+#include <folly/coro/Task.h> // @shim
diff --git a/folly/folly/experimental/coro/TimedWait.h b/folly/folly/experimental/coro/TimedWait.h
--- a/folly/folly/experimental/coro/TimedWait.h
+++ b/folly/folly/experimental/coro/TimedWait.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/TimedWait.h>
+#include <folly/coro/TimedWait.h> // @shim
diff --git a/folly/folly/experimental/coro/Timeout-inl.h b/folly/folly/experimental/coro/Timeout-inl.h
--- a/folly/folly/experimental/coro/Timeout-inl.h
+++ b/folly/folly/experimental/coro/Timeout-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Timeout-inl.h>
+#include <folly/coro/Timeout-inl.h> // @shim
diff --git a/folly/folly/experimental/coro/Timeout.h b/folly/folly/experimental/coro/Timeout.h
--- a/folly/folly/experimental/coro/Timeout.h
+++ b/folly/folly/experimental/coro/Timeout.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Timeout.h>
+#include <folly/coro/Timeout.h> // @shim
diff --git a/folly/folly/experimental/coro/Traits.h b/folly/folly/experimental/coro/Traits.h
--- a/folly/folly/experimental/coro/Traits.h
+++ b/folly/folly/experimental/coro/Traits.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Traits.h>
+#include <folly/coro/Traits.h> // @shim
diff --git a/folly/folly/experimental/coro/Transform-inl.h b/folly/folly/experimental/coro/Transform-inl.h
--- a/folly/folly/experimental/coro/Transform-inl.h
+++ b/folly/folly/experimental/coro/Transform-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Transform-inl.h>
+#include <folly/coro/Transform-inl.h> // @shim
diff --git a/folly/folly/experimental/coro/Transform.h b/folly/folly/experimental/coro/Transform.h
--- a/folly/folly/experimental/coro/Transform.h
+++ b/folly/folly/experimental/coro/Transform.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/Transform.h>
+#include <folly/coro/Transform.h> // @shim
diff --git a/folly/folly/experimental/coro/UnboundedQueue.h b/folly/folly/experimental/coro/UnboundedQueue.h
--- a/folly/folly/experimental/coro/UnboundedQueue.h
+++ b/folly/folly/experimental/coro/UnboundedQueue.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/UnboundedQueue.h>
+#include <folly/coro/UnboundedQueue.h> // @shim
diff --git a/folly/folly/experimental/coro/ViaIfAsync.h b/folly/folly/experimental/coro/ViaIfAsync.h
--- a/folly/folly/experimental/coro/ViaIfAsync.h
+++ b/folly/folly/experimental/coro/ViaIfAsync.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/ViaIfAsync.h>
+#include <folly/coro/ViaIfAsync.h> // @shim
diff --git a/folly/folly/experimental/coro/WithAsyncStack.h b/folly/folly/experimental/coro/WithAsyncStack.h
--- a/folly/folly/experimental/coro/WithAsyncStack.h
+++ b/folly/folly/experimental/coro/WithAsyncStack.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/WithAsyncStack.h>
+#include <folly/coro/WithAsyncStack.h> // @shim
diff --git a/folly/folly/experimental/coro/WithCancellation.h b/folly/folly/experimental/coro/WithCancellation.h
--- a/folly/folly/experimental/coro/WithCancellation.h
+++ b/folly/folly/experimental/coro/WithCancellation.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/WithCancellation.h>
+#include <folly/coro/WithCancellation.h> // @shim
diff --git a/folly/folly/experimental/coro/detail/Barrier.h b/folly/folly/experimental/coro/detail/Barrier.h
--- a/folly/folly/experimental/coro/detail/Barrier.h
+++ b/folly/folly/experimental/coro/detail/Barrier.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/detail/Barrier.h>
+#include <folly/coro/detail/Barrier.h> // @shim
diff --git a/folly/folly/experimental/coro/detail/BarrierTask.h b/folly/folly/experimental/coro/detail/BarrierTask.h
--- a/folly/folly/experimental/coro/detail/BarrierTask.h
+++ b/folly/folly/experimental/coro/detail/BarrierTask.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/detail/BarrierTask.h>
+#include <folly/coro/detail/BarrierTask.h> // @shim
diff --git a/folly/folly/experimental/coro/detail/CurrentAsyncFrame.h b/folly/folly/experimental/coro/detail/CurrentAsyncFrame.h
--- a/folly/folly/experimental/coro/detail/CurrentAsyncFrame.h
+++ b/folly/folly/experimental/coro/detail/CurrentAsyncFrame.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/detail/CurrentAsyncFrame.h>
+#include <folly/coro/detail/CurrentAsyncFrame.h> // @shim
diff --git a/folly/folly/experimental/coro/detail/Helpers.h b/folly/folly/experimental/coro/detail/Helpers.h
--- a/folly/folly/experimental/coro/detail/Helpers.h
+++ b/folly/folly/experimental/coro/detail/Helpers.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/detail/Helpers.h>
+#include <folly/coro/detail/Helpers.h> // @shim
diff --git a/folly/folly/experimental/coro/detail/InlineTask.h b/folly/folly/experimental/coro/detail/InlineTask.h
--- a/folly/folly/experimental/coro/detail/InlineTask.h
+++ b/folly/folly/experimental/coro/detail/InlineTask.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/detail/InlineTask.h>
+#include <folly/coro/detail/InlineTask.h> // @shim
diff --git a/folly/folly/experimental/coro/detail/Malloc.h b/folly/folly/experimental/coro/detail/Malloc.h
--- a/folly/folly/experimental/coro/detail/Malloc.h
+++ b/folly/folly/experimental/coro/detail/Malloc.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/detail/Malloc.h>
+#include <folly/coro/detail/Malloc.h> // @shim
diff --git a/folly/folly/experimental/coro/detail/ManualLifetime.h b/folly/folly/experimental/coro/detail/ManualLifetime.h
--- a/folly/folly/experimental/coro/detail/ManualLifetime.h
+++ b/folly/folly/experimental/coro/detail/ManualLifetime.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/detail/ManualLifetime.h>
+#include <folly/coro/detail/ManualLifetime.h> // @shim
diff --git a/folly/folly/experimental/coro/detail/Traits.h b/folly/folly/experimental/coro/detail/Traits.h
--- a/folly/folly/experimental/coro/detail/Traits.h
+++ b/folly/folly/experimental/coro/detail/Traits.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/coro/detail/Traits.h>
+#include <folly/coro/detail/Traits.h> // @shim
diff --git a/folly/folly/experimental/crypto/Blake2xb.h b/folly/folly/experimental/crypto/Blake2xb.h
--- a/folly/folly/experimental/crypto/Blake2xb.h
+++ b/folly/folly/experimental/crypto/Blake2xb.h
@@ -16,4 +16,4 @@
 
 #pragma once
 
-#include <folly/crypto/Blake2xb.h>
+#include <folly/crypto/Blake2xb.h> // @shim
diff --git a/folly/folly/experimental/crypto/LtHash.h b/folly/folly/experimental/crypto/LtHash.h
--- a/folly/folly/experimental/crypto/LtHash.h
+++ b/folly/folly/experimental/crypto/LtHash.h
@@ -16,4 +16,4 @@
 
 #pragma once
 
-#include <folly/crypto/LtHash.h>
+#include <folly/crypto/LtHash.h> // @shim
diff --git a/folly/folly/experimental/exception_tracer/ExceptionAbi.h b/folly/folly/experimental/exception_tracer/ExceptionAbi.h
--- a/folly/folly/experimental/exception_tracer/ExceptionAbi.h
+++ b/folly/folly/experimental/exception_tracer/ExceptionAbi.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/exception_tracer/ExceptionAbi.h>
+#include <folly/debugging/exception_tracer/ExceptionAbi.h> // @shim
diff --git a/folly/folly/experimental/exception_tracer/ExceptionCounterLib.h b/folly/folly/experimental/exception_tracer/ExceptionCounterLib.h
--- a/folly/folly/experimental/exception_tracer/ExceptionCounterLib.h
+++ b/folly/folly/experimental/exception_tracer/ExceptionCounterLib.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/exception_tracer/ExceptionCounterLib.h>
+#include <folly/debugging/exception_tracer/ExceptionCounterLib.h> // @shim
diff --git a/folly/folly/experimental/exception_tracer/ExceptionTracer.h b/folly/folly/experimental/exception_tracer/ExceptionTracer.h
--- a/folly/folly/experimental/exception_tracer/ExceptionTracer.h
+++ b/folly/folly/experimental/exception_tracer/ExceptionTracer.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/exception_tracer/ExceptionTracer.h>
+#include <folly/debugging/exception_tracer/ExceptionTracer.h> // @shim
diff --git a/folly/folly/experimental/exception_tracer/ExceptionTracerLib.h b/folly/folly/experimental/exception_tracer/ExceptionTracerLib.h
--- a/folly/folly/experimental/exception_tracer/ExceptionTracerLib.h
+++ b/folly/folly/experimental/exception_tracer/ExceptionTracerLib.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>
+#include <folly/debugging/exception_tracer/ExceptionTracerLib.h> // @shim
diff --git a/folly/folly/experimental/exception_tracer/SmartExceptionTracer.h b/folly/folly/experimental/exception_tracer/SmartExceptionTracer.h
--- a/folly/folly/experimental/exception_tracer/SmartExceptionTracer.h
+++ b/folly/folly/experimental/exception_tracer/SmartExceptionTracer.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/exception_tracer/SmartExceptionTracer.h>
+#include <folly/debugging/exception_tracer/SmartExceptionTracer.h> // @shim
diff --git a/folly/folly/experimental/exception_tracer/SmartExceptionTracerSingleton.h b/folly/folly/experimental/exception_tracer/SmartExceptionTracerSingleton.h
--- a/folly/folly/experimental/exception_tracer/SmartExceptionTracerSingleton.h
+++ b/folly/folly/experimental/exception_tracer/SmartExceptionTracerSingleton.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h>
+#include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h> // @shim
diff --git a/folly/folly/experimental/exception_tracer/StackTrace.h b/folly/folly/experimental/exception_tracer/StackTrace.h
--- a/folly/folly/experimental/exception_tracer/StackTrace.h
+++ b/folly/folly/experimental/exception_tracer/StackTrace.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/exception_tracer/StackTrace.h>
+#include <folly/debugging/exception_tracer/StackTrace.h> // @shim
diff --git a/folly/folly/experimental/flat_combining/FlatCombining.h b/folly/folly/experimental/flat_combining/FlatCombining.h
--- a/folly/folly/experimental/flat_combining/FlatCombining.h
+++ b/folly/folly/experimental/flat_combining/FlatCombining.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/synchronization/FlatCombining.h>
+#include <folly/synchronization/FlatCombining.h> // @shim
diff --git a/folly/folly/experimental/io/AsyncBase.h b/folly/folly/experimental/io/AsyncBase.h
--- a/folly/folly/experimental/io/AsyncBase.h
+++ b/folly/folly/experimental/io/AsyncBase.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/AsyncBase.h>
+#include <folly/io/async/AsyncBase.h> // @shim
diff --git a/folly/folly/experimental/io/AsyncIO.h b/folly/folly/experimental/io/AsyncIO.h
--- a/folly/folly/experimental/io/AsyncIO.h
+++ b/folly/folly/experimental/io/AsyncIO.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/AsyncIO.h>
+#include <folly/io/async/AsyncIO.h> // @shim
diff --git a/folly/folly/experimental/io/AsyncIoUringSocket.h b/folly/folly/experimental/io/AsyncIoUringSocket.h
--- a/folly/folly/experimental/io/AsyncIoUringSocket.h
+++ b/folly/folly/experimental/io/AsyncIoUringSocket.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/AsyncIoUringSocket.h>
+#include <folly/io/async/AsyncIoUringSocket.h> // @shim
diff --git a/folly/folly/experimental/io/AsyncIoUringSocketFactory.h b/folly/folly/experimental/io/AsyncIoUringSocketFactory.h
--- a/folly/folly/experimental/io/AsyncIoUringSocketFactory.h
+++ b/folly/folly/experimental/io/AsyncIoUringSocketFactory.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/AsyncIoUringSocketFactory.h>
+#include <folly/io/async/AsyncIoUringSocketFactory.h> // @shim
diff --git a/folly/folly/experimental/io/Epoll.h b/folly/folly/experimental/io/Epoll.h
--- a/folly/folly/experimental/io/Epoll.h
+++ b/folly/folly/experimental/io/Epoll.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/Epoll.h>
+#include <folly/io/async/Epoll.h> // @shim
diff --git a/folly/folly/experimental/io/EpollBackend.h b/folly/folly/experimental/io/EpollBackend.h
--- a/folly/folly/experimental/io/EpollBackend.h
+++ b/folly/folly/experimental/io/EpollBackend.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/EpollBackend.h>
+#include <folly/io/async/EpollBackend.h> // @shim
diff --git a/folly/folly/experimental/io/EventBasePoller.h b/folly/folly/experimental/io/EventBasePoller.h
--- a/folly/folly/experimental/io/EventBasePoller.h
+++ b/folly/folly/experimental/io/EventBasePoller.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/EventBasePoller.h>
+#include <folly/io/async/EventBasePoller.h> // @shim
diff --git a/folly/folly/experimental/io/FsUtil.h b/folly/folly/experimental/io/FsUtil.h
--- a/folly/folly/experimental/io/FsUtil.h
+++ b/folly/folly/experimental/io/FsUtil.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/FsUtil.h>
+#include <folly/io/FsUtil.h> // @shim
diff --git a/folly/folly/experimental/io/HugePages.h b/folly/folly/experimental/io/HugePages.h
--- a/folly/folly/experimental/io/HugePages.h
+++ b/folly/folly/experimental/io/HugePages.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/HugePages.h>
+#include <folly/io/HugePages.h> // @shim
diff --git a/folly/folly/experimental/io/IoUring.h b/folly/folly/experimental/io/IoUring.h
--- a/folly/folly/experimental/io/IoUring.h
+++ b/folly/folly/experimental/io/IoUring.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/IoUring.h>
+#include <folly/io/async/IoUring.h> // @shim
diff --git a/folly/folly/experimental/io/IoUringBackend.h b/folly/folly/experimental/io/IoUringBackend.h
--- a/folly/folly/experimental/io/IoUringBackend.h
+++ b/folly/folly/experimental/io/IoUringBackend.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/IoUringBackend.h>
+#include <folly/io/async/IoUringBackend.h> // @shim
diff --git a/folly/folly/experimental/io/IoUringBase.h b/folly/folly/experimental/io/IoUringBase.h
--- a/folly/folly/experimental/io/IoUringBase.h
+++ b/folly/folly/experimental/io/IoUringBase.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/IoUringBase.h>
+#include <folly/io/async/IoUringBase.h> // @shim
diff --git a/folly/folly/experimental/io/IoUringEvent.h b/folly/folly/experimental/io/IoUringEvent.h
--- a/folly/folly/experimental/io/IoUringEvent.h
+++ b/folly/folly/experimental/io/IoUringEvent.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/IoUringEvent.h>
+#include <folly/io/async/IoUringEvent.h> // @shim
diff --git a/folly/folly/experimental/io/IoUringEventBaseLocal.h b/folly/folly/experimental/io/IoUringEventBaseLocal.h
--- a/folly/folly/experimental/io/IoUringEventBaseLocal.h
+++ b/folly/folly/experimental/io/IoUringEventBaseLocal.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/IoUringEventBaseLocal.h>
+#include <folly/io/async/IoUringEventBaseLocal.h> // @shim
diff --git a/folly/folly/experimental/io/IoUringProvidedBufferRing.h b/folly/folly/experimental/io/IoUringProvidedBufferRing.h
--- a/folly/folly/experimental/io/IoUringProvidedBufferRing.h
+++ b/folly/folly/experimental/io/IoUringProvidedBufferRing.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/IoUringProvidedBufferRing.h>
+#include <folly/io/async/IoUringProvidedBufferRing.h> // @shim
diff --git a/folly/folly/experimental/io/Liburing.h b/folly/folly/experimental/io/Liburing.h
--- a/folly/folly/experimental/io/Liburing.h
+++ b/folly/folly/experimental/io/Liburing.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/Liburing.h>
+#include <folly/io/async/Liburing.h> // @shim
diff --git a/folly/folly/experimental/io/MuxIOThreadPoolExecutor.h b/folly/folly/experimental/io/MuxIOThreadPoolExecutor.h
--- a/folly/folly/experimental/io/MuxIOThreadPoolExecutor.h
+++ b/folly/folly/experimental/io/MuxIOThreadPoolExecutor.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/MuxIOThreadPoolExecutor.h>
+#include <folly/io/async/MuxIOThreadPoolExecutor.h> // @shim
diff --git a/folly/folly/experimental/io/SimpleAsyncIO.h b/folly/folly/experimental/io/SimpleAsyncIO.h
--- a/folly/folly/experimental/io/SimpleAsyncIO.h
+++ b/folly/folly/experimental/io/SimpleAsyncIO.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/io/async/SimpleAsyncIO.h>
+#include <folly/io/async/SimpleAsyncIO.h> // @shim
diff --git a/folly/folly/experimental/observer/CoreCachedObserver.h b/folly/folly/experimental/observer/CoreCachedObserver.h
--- a/folly/folly/experimental/observer/CoreCachedObserver.h
+++ b/folly/folly/experimental/observer/CoreCachedObserver.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/CoreCachedObserver.h>
+#include <folly/observer/CoreCachedObserver.h> // @shim
diff --git a/folly/folly/experimental/observer/HazptrObserver.h b/folly/folly/experimental/observer/HazptrObserver.h
--- a/folly/folly/experimental/observer/HazptrObserver.h
+++ b/folly/folly/experimental/observer/HazptrObserver.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/HazptrObserver.h>
+#include <folly/observer/HazptrObserver.h> // @shim
diff --git a/folly/folly/experimental/observer/Observable-inl.h b/folly/folly/experimental/observer/Observable-inl.h
--- a/folly/folly/experimental/observer/Observable-inl.h
+++ b/folly/folly/experimental/observer/Observable-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/Observable-inl.h>
+#include <folly/observer/Observable-inl.h> // @shim
diff --git a/folly/folly/experimental/observer/Observable.h b/folly/folly/experimental/observer/Observable.h
--- a/folly/folly/experimental/observer/Observable.h
+++ b/folly/folly/experimental/observer/Observable.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/Observable.h>
+#include <folly/observer/Observable.h> // @shim
diff --git a/folly/folly/experimental/observer/Observer-inl.h b/folly/folly/experimental/observer/Observer-inl.h
--- a/folly/folly/experimental/observer/Observer-inl.h
+++ b/folly/folly/experimental/observer/Observer-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/Observer-inl.h>
+#include <folly/observer/Observer-inl.h> // @shim
diff --git a/folly/folly/experimental/observer/Observer-pre.h b/folly/folly/experimental/observer/Observer-pre.h
--- a/folly/folly/experimental/observer/Observer-pre.h
+++ b/folly/folly/experimental/observer/Observer-pre.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/Observer-pre.h>
+#include <folly/observer/Observer-pre.h> // @shim
diff --git a/folly/folly/experimental/observer/Observer.h b/folly/folly/experimental/observer/Observer.h
--- a/folly/folly/experimental/observer/Observer.h
+++ b/folly/folly/experimental/observer/Observer.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/Observer.h>
+#include <folly/observer/Observer.h> // @shim
diff --git a/folly/folly/experimental/observer/ReadMostlyTLObserver.h b/folly/folly/experimental/observer/ReadMostlyTLObserver.h
--- a/folly/folly/experimental/observer/ReadMostlyTLObserver.h
+++ b/folly/folly/experimental/observer/ReadMostlyTLObserver.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/ReadMostlyTLObserver.h>
+#include <folly/observer/ReadMostlyTLObserver.h> // @shim
diff --git a/folly/folly/experimental/observer/SimpleObservable-inl.h b/folly/folly/experimental/observer/SimpleObservable-inl.h
--- a/folly/folly/experimental/observer/SimpleObservable-inl.h
+++ b/folly/folly/experimental/observer/SimpleObservable-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/SimpleObservable-inl.h>
+#include <folly/observer/SimpleObservable-inl.h> // @shim
diff --git a/folly/folly/experimental/observer/SimpleObservable.h b/folly/folly/experimental/observer/SimpleObservable.h
--- a/folly/folly/experimental/observer/SimpleObservable.h
+++ b/folly/folly/experimental/observer/SimpleObservable.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/SimpleObservable.h>
+#include <folly/observer/SimpleObservable.h> // @shim
diff --git a/folly/folly/experimental/observer/WithJitter-inl.h b/folly/folly/experimental/observer/WithJitter-inl.h
--- a/folly/folly/experimental/observer/WithJitter-inl.h
+++ b/folly/folly/experimental/observer/WithJitter-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/WithJitter-inl.h>
+#include <folly/observer/WithJitter-inl.h> // @shim
diff --git a/folly/folly/experimental/observer/WithJitter.h b/folly/folly/experimental/observer/WithJitter.h
--- a/folly/folly/experimental/observer/WithJitter.h
+++ b/folly/folly/experimental/observer/WithJitter.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/WithJitter.h>
+#include <folly/observer/WithJitter.h> // @shim
diff --git a/folly/folly/experimental/observer/detail/Core.h b/folly/folly/experimental/observer/detail/Core.h
--- a/folly/folly/experimental/observer/detail/Core.h
+++ b/folly/folly/experimental/observer/detail/Core.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/detail/Core.h>
+#include <folly/observer/detail/Core.h> // @shim
diff --git a/folly/folly/experimental/observer/detail/GraphCycleDetector.h b/folly/folly/experimental/observer/detail/GraphCycleDetector.h
--- a/folly/folly/experimental/observer/detail/GraphCycleDetector.h
+++ b/folly/folly/experimental/observer/detail/GraphCycleDetector.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/detail/GraphCycleDetector.h>
+#include <folly/observer/detail/GraphCycleDetector.h> // @shim
diff --git a/folly/folly/experimental/observer/detail/ObserverManager.h b/folly/folly/experimental/observer/detail/ObserverManager.h
--- a/folly/folly/experimental/observer/detail/ObserverManager.h
+++ b/folly/folly/experimental/observer/detail/ObserverManager.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/observer/detail/ObserverManager.h>
+#include <folly/observer/detail/ObserverManager.h> // @shim
diff --git a/folly/folly/experimental/settings/Immutables.h b/folly/folly/experimental/settings/Immutables.h
--- a/folly/folly/experimental/settings/Immutables.h
+++ b/folly/folly/experimental/settings/Immutables.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/settings/Immutables.h>
+#include <folly/settings/Immutables.h> // @shim
diff --git a/folly/folly/experimental/settings/Settings.h b/folly/folly/experimental/settings/Settings.h
--- a/folly/folly/experimental/settings/Settings.h
+++ b/folly/folly/experimental/settings/Settings.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/settings/Settings.h>
+#include <folly/settings/Settings.h> // @shim
diff --git a/folly/folly/experimental/settings/Types.h b/folly/folly/experimental/settings/Types.h
--- a/folly/folly/experimental/settings/Types.h
+++ b/folly/folly/experimental/settings/Types.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/settings/Types.h>
+#include <folly/settings/Types.h> // @shim
diff --git a/folly/folly/experimental/settings/detail/SettingsImpl.h b/folly/folly/experimental/settings/detail/SettingsImpl.h
--- a/folly/folly/experimental/settings/detail/SettingsImpl.h
+++ b/folly/folly/experimental/settings/detail/SettingsImpl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/settings/detail/SettingsImpl.h>
+#include <folly/settings/detail/SettingsImpl.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/Dwarf.h b/folly/folly/experimental/symbolizer/Dwarf.h
--- a/folly/folly/experimental/symbolizer/Dwarf.h
+++ b/folly/folly/experimental/symbolizer/Dwarf.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/Dwarf.h>
+#include <folly/debugging/symbolizer/Dwarf.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/DwarfImpl.h b/folly/folly/experimental/symbolizer/DwarfImpl.h
--- a/folly/folly/experimental/symbolizer/DwarfImpl.h
+++ b/folly/folly/experimental/symbolizer/DwarfImpl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/DwarfImpl.h>
+#include <folly/debugging/symbolizer/DwarfImpl.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/DwarfLineNumberVM.h b/folly/folly/experimental/symbolizer/DwarfLineNumberVM.h
--- a/folly/folly/experimental/symbolizer/DwarfLineNumberVM.h
+++ b/folly/folly/experimental/symbolizer/DwarfLineNumberVM.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/DwarfLineNumberVM.h>
+#include <folly/debugging/symbolizer/DwarfLineNumberVM.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/DwarfSection.h b/folly/folly/experimental/symbolizer/DwarfSection.h
--- a/folly/folly/experimental/symbolizer/DwarfSection.h
+++ b/folly/folly/experimental/symbolizer/DwarfSection.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/DwarfSection.h>
+#include <folly/debugging/symbolizer/DwarfSection.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/DwarfUtil.h b/folly/folly/experimental/symbolizer/DwarfUtil.h
--- a/folly/folly/experimental/symbolizer/DwarfUtil.h
+++ b/folly/folly/experimental/symbolizer/DwarfUtil.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/DwarfUtil.h>
+#include <folly/debugging/symbolizer/DwarfUtil.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/Elf-inl.h b/folly/folly/experimental/symbolizer/Elf-inl.h
--- a/folly/folly/experimental/symbolizer/Elf-inl.h
+++ b/folly/folly/experimental/symbolizer/Elf-inl.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/Elf-inl.h>
+#include <folly/debugging/symbolizer/Elf-inl.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/Elf.h b/folly/folly/experimental/symbolizer/Elf.h
--- a/folly/folly/experimental/symbolizer/Elf.h
+++ b/folly/folly/experimental/symbolizer/Elf.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/Elf.h>
+#include <folly/debugging/symbolizer/Elf.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/ElfCache.h b/folly/folly/experimental/symbolizer/ElfCache.h
--- a/folly/folly/experimental/symbolizer/ElfCache.h
+++ b/folly/folly/experimental/symbolizer/ElfCache.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/ElfCache.h>
+#include <folly/debugging/symbolizer/ElfCache.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/LineReader.h b/folly/folly/experimental/symbolizer/LineReader.h
--- a/folly/folly/experimental/symbolizer/LineReader.h
+++ b/folly/folly/experimental/symbolizer/LineReader.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/LineReader.h>
+#include <folly/debugging/symbolizer/LineReader.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/SignalHandler.h b/folly/folly/experimental/symbolizer/SignalHandler.h
--- a/folly/folly/experimental/symbolizer/SignalHandler.h
+++ b/folly/folly/experimental/symbolizer/SignalHandler.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/SignalHandler.h>
+#include <folly/debugging/symbolizer/SignalHandler.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/StackTrace.h b/folly/folly/experimental/symbolizer/StackTrace.h
--- a/folly/folly/experimental/symbolizer/StackTrace.h
+++ b/folly/folly/experimental/symbolizer/StackTrace.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/StackTrace.h>
+#include <folly/debugging/symbolizer/StackTrace.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/SymbolizePrinter.h b/folly/folly/experimental/symbolizer/SymbolizePrinter.h
--- a/folly/folly/experimental/symbolizer/SymbolizePrinter.h
+++ b/folly/folly/experimental/symbolizer/SymbolizePrinter.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/SymbolizePrinter.h>
+#include <folly/debugging/symbolizer/SymbolizePrinter.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/SymbolizedFrame.h b/folly/folly/experimental/symbolizer/SymbolizedFrame.h
--- a/folly/folly/experimental/symbolizer/SymbolizedFrame.h
+++ b/folly/folly/experimental/symbolizer/SymbolizedFrame.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/SymbolizedFrame.h>
+#include <folly/debugging/symbolizer/SymbolizedFrame.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/Symbolizer.h b/folly/folly/experimental/symbolizer/Symbolizer.h
--- a/folly/folly/experimental/symbolizer/Symbolizer.h
+++ b/folly/folly/experimental/symbolizer/Symbolizer.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/Symbolizer.h>
+#include <folly/debugging/symbolizer/Symbolizer.h> // @shim
diff --git a/folly/folly/experimental/symbolizer/detail/Debug.h b/folly/folly/experimental/symbolizer/detail/Debug.h
--- a/folly/folly/experimental/symbolizer/detail/Debug.h
+++ b/folly/folly/experimental/symbolizer/detail/Debug.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/debugging/symbolizer/detail/Debug.h>
+#include <folly/debugging/symbolizer/detail/Debug.h> // @shim
diff --git a/folly/folly/ext/buck2/test_ext.cpp b/folly/folly/ext/buck2/test_ext.cpp
deleted file mode 100644
--- a/folly/folly/ext/buck2/test_ext.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/ext/test_ext.h>
-
-#include <fstream>
-#include <sstream>
-
-#include <boost/filesystem.hpp>
-
-#include <folly/experimental/io/FsUtil.h>
-#include <folly/json/dynamic.h>
-#include <folly/json/json.h>
-
-namespace folly {
-namespace ext {
-
-static std::string test_find_resource_buck2(std::string const& resource) {
-  auto const exe = fs::executable_path();
-  auto const bfn = exe.filename().string() + ".resources.json";
-  auto const res = exe.parent_path() / bfn;
-  std::stringstream buf;
-  buf << std::ifstream(res.string()).rdbuf();
-  auto const dyn = folly::parseJson(buf.str());
-  auto const tgt = dyn[resource].asString();
-  auto const ret = exe.parent_path() / tgt;
-  return ret.string();
-}
-
-static const auto& test_find_resource_ctor_ = //
-    test_find_resource = test_find_resource_buck2;
-
-} // namespace ext
-} // namespace folly
diff --git a/folly/folly/ext/test_ext.h b/folly/folly/ext/test_ext.h
--- a/folly/folly/ext/test_ext.h
+++ b/folly/folly/ext/test_ext.h
@@ -16,12 +16,12 @@
 
 #pragma once
 
-#include <string>
+#include <string_view>
 
 namespace folly {
 namespace ext {
 
-using test_find_resource_t = std::string(std::string const&);
+using test_find_resource_t = std::string(std::string_view);
 extern test_find_resource_t* test_find_resource;
 
 } // namespace ext
diff --git a/folly/folly/external/fast-crc32/avx512_crc32c_v8s3x4.h b/folly/folly/external/fast-crc32/avx512_crc32c_v8s3x4.h
--- a/folly/folly/external/fast-crc32/avx512_crc32c_v8s3x4.h
+++ b/folly/folly/external/fast-crc32/avx512_crc32c_v8s3x4.h
@@ -3,7 +3,7 @@
 #include <cstdint>
 
 #include <folly/Portability.h>
-#if defined(FOLLY_X64) && FOLLY_SSE_PREREQ(4, 2) && defined(__AVX512VL__) && defined(__AVX512F__)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2) && defined(__AVX512VL__) && defined(__AVX512F__)
 #define FOLLY_ENABLE_AVX512_CRC32C_V8S3X4 1
 #endif
 
diff --git a/folly/folly/external/fast-crc32/sse_crc32c_v8s3x3.h b/folly/folly/external/fast-crc32/sse_crc32c_v8s3x3.h
--- a/folly/folly/external/fast-crc32/sse_crc32c_v8s3x3.h
+++ b/folly/folly/external/fast-crc32/sse_crc32c_v8s3x3.h
@@ -3,7 +3,7 @@
 #include <cstdint>
 
 #include <folly/Portability.h>
-#if defined(FOLLY_X64) && FOLLY_SSE_PREREQ(4, 2)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 #define FOLLY_ENABLE_SSE42_CRC32C_V8S3X3 1
 #endif
 
diff --git a/folly/folly/fibers/AddTasks.h b/folly/folly/fibers/AddTasks.h
--- a/folly/folly/fibers/AddTasks.h
+++ b/folly/folly/fibers/AddTasks.h
@@ -49,7 +49,7 @@
 template <typename T>
 class TaskIterator {
  public:
-  typedef T value_type;
+  using value_type = T;
 
   TaskIterator() : fm_(FiberManager::getFiberManager()) {}
 
@@ -60,6 +60,8 @@
   // movable
   TaskIterator(TaskIterator&& other) noexcept;
   TaskIterator& operator=(TaskIterator&& other) = delete;
+
+  ~TaskIterator() = default;
 
   /**
    * Add one more task to the TaskIterator.
diff --git a/folly/folly/fibers/AtomicBatchDispatcher-inl.h b/folly/folly/fibers/AtomicBatchDispatcher-inl.h
--- a/folly/folly/fibers/AtomicBatchDispatcher-inl.h
+++ b/folly/folly/fibers/AtomicBatchDispatcher-inl.h
@@ -91,9 +91,10 @@
       // and make sure that we have the expected number of results returned
       auto results = dispatchFunction_(std::move(inputs));
       if (results.size() != expectedCount_) {
-        return setExceptionResults(make_exception_wrapper<ABDUsageException>(
-            detail::createUnexpectedNumResultsABDUsageExMsg(
-                expectedCount_, results.size())));
+        return setExceptionResults(
+            make_exception_wrapper<ABDUsageException>(
+                detail::createUnexpectedNumResultsABDUsageExMsg(
+                    expectedCount_, results.size())));
       }
 
       // Fulfill the promises with the results from the batch dispatch
diff --git a/folly/folly/fibers/Baton.h b/folly/folly/fibers/Baton.h
--- a/folly/folly/fibers/Baton.h
+++ b/folly/folly/fibers/Baton.h
@@ -50,7 +50,7 @@
 
   ~Baton() noexcept = default;
 
-  bool ready() const {
+  bool ready() const noexcept {
     auto state = waiter_.load();
     return state == POSTED;
   }
@@ -58,6 +58,8 @@
   /**
    * Registers a waiter for the baton. The waiter will be notified when
    * the baton is posted.
+   *
+   * Precondition: No waiter is currently registered. Throws logic_error.
    */
   void setWaiter(Waiter& waiter);
 
@@ -263,6 +265,24 @@
 
 #if FOLLY_HAS_COROUTINES
 namespace detail {
+
+// `BatonAwaitableWaiter` intentionally does NOT support value-only await
+// protocols (`value_or_error`, `co_awaitTry`) and MUST NOT gain them.
+//
+// Value-only protocols require `await_ready` and `await_suspend` to be
+// noexcept, but this can't be arranged for Baton:
+//  - Making `await_suspend` noexcept is a breaking change -- existing code
+//    may rely on `Baton` throwing on double-wait misuse.
+//  - Value-only support could in theory be implemented via awaitable wrappers
+//    like `fatal_if_await_throws` or `unsafe_throwing_await` (ref D91285418).
+//    But, `Baton` is immovable (has `std::atomic`) and awaitable only by
+//    lvalue, so it's especially annoying to wrap.
+//
+// The best fix for value-only support -- have `Baton` emit an explicit
+// must-use-immediately awaitable instead of being awaited by lvalue. This
+// would also let us deprecate the current lvalue-await API, and once it's
+// gone, much `folly/coro` code simplifies (see `ViaIfAsync.h` for breadcrumbs
+// on lvalue-awaitable handling).
 class BatonAwaitableWaiter : public Baton::Waiter {
  public:
   explicit BatonAwaitableWaiter(Baton& baton) : baton_(baton) {}
@@ -272,10 +292,12 @@
     h_();
   }
 
-  bool await_ready() const { return baton_.ready(); }
+  bool await_ready() const noexcept { return baton_.ready(); }
 
   void await_resume() {}
 
+  // Precondition: No waiter may already be registered, or `setWaiter` will
+  // throw `logic_error`.  The class doc explains why throwing is a problem.
   void await_suspend(coro::coroutine_handle<> h) {
     assert(!h_);
     h_ = std::move(h);
@@ -289,7 +311,7 @@
 } // namespace detail
 
 inline detail::BatonAwaitableWaiter /* implicit */ operator co_await(
-    Baton & baton) {
+    Baton& baton) {
   return detail::BatonAwaitableWaiter(baton);
 }
 #endif
diff --git a/folly/folly/fibers/Fiber.cpp b/folly/folly/fibers/Fiber.cpp
--- a/folly/folly/fibers/Fiber.cpp
+++ b/folly/folly/fibers/Fiber.cpp
@@ -18,12 +18,10 @@
 
 #include <algorithm>
 #include <cstring>
-#include <stdexcept>
 
 #include <glog/logging.h>
 
 #include <folly/Likely.h>
-#include <folly/Portability.h>
 #include <folly/fibers/FiberManagerInternal.h>
 #include <folly/portability/SysSyscall.h>
 #include <folly/portability/Unistd.h>
diff --git a/folly/folly/fibers/FiberManager.cpp b/folly/folly/fibers/FiberManager.cpp
--- a/folly/folly/fibers/FiberManager.cpp
+++ b/folly/folly/fibers/FiberManager.cpp
@@ -19,7 +19,6 @@
 #include <csignal>
 
 #include <cassert>
-#include <stdexcept>
 
 #include <glog/logging.h>
 
diff --git a/folly/folly/fibers/FiberManagerInternal.h b/folly/folly/fibers/FiberManagerInternal.h
--- a/folly/folly/fibers/FiberManagerInternal.h
+++ b/folly/folly/fibers/FiberManagerInternal.h
@@ -482,9 +482,9 @@
 
   size_t recordStackPosition(size_t position);
 
-  typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
-  typedef folly::IntrusiveList<Fiber, &Fiber::globalListHook_>
-      GlobalFiberTailQueue;
+  using FiberTailQueue = folly::IntrusiveList<Fiber, &Fiber::listHook_>;
+  using GlobalFiberTailQueue =
+      folly::IntrusiveList<Fiber, &Fiber::globalListHook_>;
 
   Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
   /**
diff --git a/folly/folly/fibers/ForEach-inl.h b/folly/folly/fibers/ForEach-inl.h
--- a/folly/folly/fibers/ForEach-inl.h
+++ b/folly/folly/fibers/ForEach-inl.h
@@ -43,7 +43,7 @@
     return;
   }
 
-  typedef typename std::iterator_traits<InputIterator>::value_type FuncType;
+  using FuncType = typename std::iterator_traits<InputIterator>::value_type;
 
   size_t tasksTodo = 1;
   std::exception_ptr e;
diff --git a/folly/folly/fibers/GenericBaton.h b/folly/folly/fibers/GenericBaton.h
--- a/folly/folly/fibers/GenericBaton.h
+++ b/folly/folly/fibers/GenericBaton.h
@@ -23,6 +23,6 @@
 namespace folly {
 namespace fibers {
 
-typedef Baton GenericBaton;
+using GenericBaton = Baton;
 } // namespace fibers
 } // namespace folly
diff --git a/folly/folly/fibers/GuardPageAllocator.cpp b/folly/folly/fibers/GuardPageAllocator.cpp
--- a/folly/folly/fibers/GuardPageAllocator.cpp
+++ b/folly/folly/fibers/GuardPageAllocator.cpp
@@ -94,9 +94,11 @@
     auto p = freeList_.back().first;
     if (!freeList_.back().second) {
       PCHECK(0 == ::mprotect(p, pagesize() * guardPagesPerStack_, PROT_NONE));
-      protectedRanges().wlock()->insert(std::make_pair(
-          reinterpret_cast<intptr_t>(p),
-          reinterpret_cast<intptr_t>(p + pagesize() * guardPagesPerStack_)));
+      protectedRanges().wlock()->insert(
+          std::make_pair(
+              reinterpret_cast<intptr_t>(p),
+              reinterpret_cast<intptr_t>(
+                  p + pagesize() * guardPagesPerStack_)));
     }
     freeList_.pop_back();
 
@@ -139,10 +141,11 @@
     assert(storage_);
     protectedRanges().withWLock([&](auto& ranges) {
       for (const auto& item : freeList_) {
-        ranges.erase(std::make_pair(
-            reinterpret_cast<intptr_t>(item.first),
-            reinterpret_cast<intptr_t>(
-                item.first + pagesize() * guardPagesPerStack_)));
+        ranges.erase(
+            std::make_pair(
+                reinterpret_cast<intptr_t>(item.first),
+                reinterpret_cast<intptr_t>(
+                    item.first + pagesize() * guardPagesPerStack_)));
       }
     });
     PCHECK(0 == ::munmap(storage_, allocSize_ * kNumGuarded));
@@ -245,32 +248,34 @@
   return getCreated;
 }
 
-void installSignalHandler() {
+void maybeInstallGuardPageSignalHandler() {
   static std::once_flag onceFlag;
-  std::call_once(onceFlag, []() {
-    if (isInJVM()) {
-      // Don't install signal handler, since JVM internal signal handler doesn't
-      // work with SA_ONSTACK
-      return;
-    }
-
-    struct sigaction sa;
-    memset(&sa, 0, sizeof(sa));
-    sigemptyset(&sa.sa_mask);
-    // By default signal handlers are run on the signaled thread's stack.
-    // In case of stack overflow running the SIGSEGV signal handler on
-    // the same stack leads to another SIGSEGV and crashes the program.
-    // Use SA_ONSTACK, so alternate stack is used (only if configured via
-    // sigaltstack).
-    sa.sa_flags |= SA_SIGINFO | SA_ONSTACK;
-    sa.sa_sigaction = &sigsegvSignalHandler;
-    sigaction(SIGSEGV, &sa, &oldSigsegvAction);
-  });
+  std::call_once(onceFlag, installGuardPageSignalHandler);
 }
 } // namespace
 
-#endif
+void installGuardPageSignalHandler() {
+  if (isInJVM()) {
+    // Don't install signal handler, since JVM internal signal handler doesn't
+    // work with SA_ONSTACK
+    return;
+  }
 
+  struct sigaction sa;
+  memset(&sa, 0, sizeof(sa));
+  sigemptyset(&sa.sa_mask);
+  // By default signal handlers are run on the signaled thread's stack.
+  // In case of stack overflow running the SIGSEGV signal handler on
+  // the same stack leads to another SIGSEGV and crashes the program.
+  // Use SA_ONSTACK, so alternate stack is used (only if configured via
+  // sigaltstack).
+  sa.sa_flags |= SA_SIGINFO | SA_ONSTACK;
+  sa.sa_sigaction = &sigsegvSignalHandler;
+  sigaction(SIGSEGV, &sa, &oldSigsegvAction);
+}
+
+#endif // _WIN32
+
 /*
  * RAII Wrapper around a StackCache that calls
  * CacheManager::giveBack() on destruction.
@@ -334,7 +339,7 @@
 GuardPageAllocator::GuardPageAllocator(size_t guardPagesPerStack)
     : guardPagesPerStack_(guardPagesPerStack) {
 #ifndef _WIN32
-  installSignalHandler();
+  maybeInstallGuardPageSignalHandler();
 #endif
 }
 
diff --git a/folly/folly/fibers/GuardPageAllocator.h b/folly/folly/fibers/GuardPageAllocator.h
--- a/folly/folly/fibers/GuardPageAllocator.h
+++ b/folly/folly/fibers/GuardPageAllocator.h
@@ -53,5 +53,15 @@
   std::allocator<unsigned char> fallbackAllocator_;
   size_t guardPagesPerStack_{0};
 };
+
+#ifndef _WIN32
+/**
+ * Unconditionally install the SIGSEGV handler that detects fiber stack
+ * overflow. Can be useful if the handler has been overridden by some other code
+ * overriding the SIGSEGV handler
+ */
+void installGuardPageSignalHandler();
+#endif
+
 } // namespace fibers
 } // namespace folly
diff --git a/folly/folly/fibers/LoopController.h b/folly/folly/fibers/LoopController.h
--- a/folly/folly/fibers/LoopController.h
+++ b/folly/folly/fibers/LoopController.h
@@ -29,8 +29,8 @@
 
 class LoopController {
  public:
-  typedef std::chrono::steady_clock Clock;
-  typedef std::chrono::time_point<Clock> TimePoint;
+  using Clock = std::chrono::steady_clock;
+  using TimePoint = std::chrono::time_point<Clock>;
 
   virtual ~LoopController() {}
 
diff --git a/folly/folly/fibers/Promise-inl.h b/folly/folly/fibers/Promise-inl.h
--- a/folly/folly/fibers/Promise-inl.h
+++ b/folly/folly/fibers/Promise-inl.h
@@ -47,8 +47,9 @@
 template <class T, class BatonT>
 Promise<T, BatonT>::~Promise() {
   if (value_) {
-    setException(folly::make_exception_wrapper<std::logic_error>(
-        "promise not fulfilled"));
+    setException(
+        folly::make_exception_wrapper<std::logic_error>(
+            "promise not fulfilled"));
   }
 }
 
diff --git a/folly/folly/fibers/Promise.h b/folly/folly/fibers/Promise.h
--- a/folly/folly/fibers/Promise.h
+++ b/folly/folly/fibers/Promise.h
@@ -28,8 +28,8 @@
 template <typename T, typename BatonT = Baton>
 class Promise {
  public:
-  typedef T value_type;
-  typedef BatonT baton_type;
+  using value_type = T;
+  using baton_type = BatonT;
 
   ~Promise();
 
diff --git a/folly/folly/fibers/Semaphore.cpp b/folly/folly/fibers/Semaphore.cpp
--- a/folly/folly/fibers/Semaphore.cpp
+++ b/folly/folly/fibers/Semaphore.cpp
@@ -173,7 +173,7 @@
         // TODO: This is not unreachable code, but the compiler wrongly thinks
         // it is. Once the compiler is fixed we can remove this.
         if (cancelled) {
-          co_yield folly::coro::co_cancelled;
+          co_yield folly::coro::co_stopped_may_throw;
         }
 
         co_return;
diff --git a/folly/folly/fibers/SemaphoreBase.cpp b/folly/folly/fibers/SemaphoreBase.cpp
--- a/folly/folly/fibers/SemaphoreBase.cpp
+++ b/folly/folly/fibers/SemaphoreBase.cpp
@@ -174,7 +174,7 @@
         // we're sure that we aren't reading it concurrently with a potential
         // write from a thread requesting cancellation.
         if (cancelled) {
-          co_yield folly::coro::co_cancelled;
+          co_yield folly::coro::co_stopped_may_throw;
         }
 
         co_return;
diff --git a/folly/folly/fibers/TimedMutex-inl.h b/folly/folly/fibers/TimedMutex-inl.h
--- a/folly/folly/fibers/TimedMutex-inl.h
+++ b/folly/folly/fibers/TimedMutex-inl.h
@@ -201,31 +201,143 @@
 //
 
 template <bool ReaderPriority, typename BatonType>
-bool TimedRWMutexImpl<ReaderPriority, BatonType>::shouldReadersWait() const {
-  return state_ == State::WRITE_LOCKED ||
-      (!ReaderPriority && !write_waiters_.empty());
+class TimedRWMutexImpl<ReaderPriority, BatonType>::StateLock {
+ public:
+  // Spin until the state is not locked to apply mutation. The callback can
+  // abort at any time.
+  template <class Mutation>
+  FOLLY_ALWAYS_INLINE static bool tryMutate(
+      std::atomic<uint64_t>& stateRef, Mutation mutation) {
+    folly::detail::Sleeper sleeper;
+    auto curState = stateRef.load(std::memory_order_relaxed);
+    while (true) {
+      while (FOLLY_UNLIKELY(curState & kStateLocked)) {
+        sleeper.wait();
+        curState = stateRef.load(std::memory_order_relaxed);
+      }
+      auto newState = curState;
+      if (!mutation(newState)) {
+        return false;
+      }
+      if (stateRef.compare_exchange_strong(
+              curState,
+              newState,
+              std::memory_order_acq_rel,
+              std::memory_order_relaxed)) {
+        return true;
+      }
+    }
+  }
+
+  explicit StateLock(std::atomic<uint64_t>& stateRef) : stateRef_(stateRef) {
+    tryMutate(stateRef_, [&](uint64_t& state) {
+      state_ = (state |= kStateLocked);
+#ifndef NDEBUG
+      initialState_ = state_;
+#endif
+      return true;
+    });
+  }
+
+  ~StateLock() {
+    if (locked_) {
+      unlock();
+    }
+  }
+
+  uint64_t& state() {
+    assert(locked_);
+    return state_;
+  }
+
+  void unlock() {
+    // All state modifications while locked should be done through state().
+    assert(stateRef_.load(std::memory_order_relaxed) == initialState_);
+    // Verify reader/writer exclusion.
+    assert(!(state_ & kWriteLocked) || (state_ >> kReadersShift) == 0);
+    stateRef_.store(state_ & ~kStateLocked, std::memory_order_release);
+    locked_ = false;
+  }
+
+  StateLock(const StateLock&) = delete;
+  StateLock(StateLock&&) = delete;
+  StateLock& operator=(const StateLock&) = delete;
+  StateLock& operator=(StateLock&&) = delete;
+
+ private:
+  std::atomic<uint64_t>& stateRef_;
+  uint64_t state_;
+#ifndef NDEBUG
+  uint64_t initialState_;
+#endif
+  bool locked_ = true;
+};
+
+// invariants that must hold when the lock is not held by anyone
+template <bool ReaderPriority, typename BatonType>
+void TimedRWMutexImpl<ReaderPriority, BatonType>::verify_unlocked_properties(
+    [[maybe_unused]] StateLock& slock) const {
+  assert(slock.state() == kStateLocked); // Only bit set is the state lock.
+  assert(read_waiters_.empty());
+  assert(write_waiters_.empty());
 }
 
 template <bool ReaderPriority, typename BatonType>
+bool TimedRWMutexImpl<ReaderPriority, BatonType>::shouldReadersWait(
+    StateLock& slock) const {
+  assert(!(slock.state() & kHasWriteWaiters) == write_waiters_.empty());
+  return shouldReadersWait(slock.state());
+}
+
+template <bool ReaderPriority, typename BatonType>
+/* static */ bool
+TimedRWMutexImpl<ReaderPriority, BatonType>::shouldReadersWait(uint64_t state) {
+  return (state & kWriteLocked) ||
+      (!ReaderPriority && (state & kHasWriteWaiters));
+}
+
+template <bool ReaderPriority, typename BatonType>
+bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared_fast() {
+  return StateLock::tryMutate(state_, [](uint64_t& state) {
+    if (FOLLY_UNLIKELY(shouldReadersWait(state))) {
+      return false;
+    }
+    state += kReadersInc;
+    return true;
+  });
+}
+
+template <bool ReaderPriority, typename BatonType>
+bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_unlock_shared_fast() {
+  return StateLock::tryMutate(state_, [](uint64_t& state) {
+    auto numReaders = state >> kReadersShift;
+    assert(!(state & kWriteLocked) && numReaders > 0);
+    if (FOLLY_UNLIKELY(numReaders == 1 && (state & kHasWriteWaiters))) {
+      return false; // We may need to wake up a writer.
+    }
+    state -= kReadersInc;
+    return true;
+  });
+}
+
+template <bool ReaderPriority, typename BatonType>
 void TimedRWMutexImpl<ReaderPriority, BatonType>::lock_shared() {
-  std::unique_lock ulock{lock_};
-  if (shouldReadersWait()) {
+  if (try_lock_shared_fast()) {
+    return;
+  }
+
+  StateLock slock{state_};
+  assert(!(slock.state() & kHasWriteWaiters) == write_waiters_.empty());
+  if (shouldReadersWait(slock)) {
     MutexWaiter waiter;
     read_waiters_.push_back(waiter);
-    ulock.unlock();
+    slock.unlock();
     waiter.wait();
-    if (folly::kIsDebug) {
-      std::unique_lock assertLock{lock_};
-      assert(state_ == State::READ_LOCKED);
-    }
+    assert((state_.load(std::memory_order_relaxed) >> kReadersShift) > 0);
     return;
   }
-  assert(
-      (state_ == State::UNLOCKED && readers_ == 0) ||
-      (state_ == State::READ_LOCKED && readers_ > 0));
-  assert(read_waiters_.empty());
-  state_ = State::READ_LOCKED;
-  readers_ += 1;
+  assert(!(slock.state() & kWriteLocked));
+  slock.state() += kReadersInc;
 }
 
 template <bool ReaderPriority, typename BatonType>
@@ -239,11 +351,14 @@
 template <typename Clock, typename Duration>
 bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared_until(
     const std::chrono::time_point<Clock, Duration>& deadline) {
-  std::unique_lock ulock{lock_};
-  if (shouldReadersWait()) {
+  if (try_lock_shared_fast()) {
+    return true;
+  }
+  StateLock slock{state_};
+  if (shouldReadersWait(slock)) {
     MutexWaiter waiter;
     read_waiters_.push_back(waiter);
-    ulock.unlock();
+    slock.unlock();
 
     if (!waiter.try_wait_until(deadline)) {
       // We timed out. Two cases:
@@ -251,60 +366,67 @@
       // 2. We're not in the waiter list anymore. This could happen if the baton
       //    times out but the mutex is unlocked before we reach this code. In
       //    this case we'll pretend we got the lock on time.
-      std::unique_lock guard{lock_};
+      StateLock slock2{state_};
       if (waiter.hook.is_linked()) {
         read_waiters_.erase(read_waiters_.iterator_to(waiter));
-        guard.unlock();
+        slock2.unlock();
         waiter.wake(); // Ensure that destruction doesn't block.
         return false;
       }
     }
     return true;
   }
-  assert(
-      (state_ == State::UNLOCKED && readers_ == 0) ||
-      (state_ == State::READ_LOCKED && readers_ > 0));
-  assert(read_waiters_.empty());
-  state_ = State::READ_LOCKED;
-  readers_ += 1;
+  assert(!(slock.state() & kWriteLocked));
+  slock.state() += kReadersInc;
   return true;
 }
 
 template <bool ReaderPriority, typename BatonType>
 bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared() {
-  std::unique_lock guard{lock_};
-  if (!shouldReadersWait()) {
-    assert(
-        (state_ == State::UNLOCKED && readers_ == 0) ||
-        (state_ == State::READ_LOCKED && readers_ > 0));
-    assert(read_waiters_.empty());
-    state_ = State::READ_LOCKED;
-    readers_ += 1;
+  if (try_lock_shared_fast()) {
     return true;
   }
-  return false;
+
+  StateLock slock{state_};
+  if (shouldReadersWait(slock)) {
+    return false;
+  }
+  assert(!(slock.state() & kWriteLocked));
+  slock.state() += kReadersInc;
+  return true;
 }
 
 template <bool ReaderPriority, typename BatonType>
 void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_shared() {
-  if (kIsDebug) {
-    std::unique_lock ulock{lock_};
-    assert(state_ == State::READ_LOCKED);
+  if (try_unlock_shared_fast()) {
+    return;
   }
+
+  assert((state_.load(std::memory_order_relaxed) >> kReadersShift) > 0);
   unlock_();
 }
 
 template <bool ReaderPriority, typename BatonType>
+bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_(StateLock& slock) {
+  if (!(slock.state() & kWriteLocked) &&
+      ((slock.state() >> kReadersShift) == 0)) {
+    verify_unlocked_properties(slock);
+    slock.state() |= kWriteLocked;
+    return true;
+  }
+  return false;
+}
+
+template <bool ReaderPriority, typename BatonType>
 void TimedRWMutexImpl<ReaderPriority, BatonType>::lock() {
-  std::unique_lock ulock{lock_};
-  if (state_ == State::UNLOCKED) {
-    verify_unlocked_properties();
-    state_ = State::WRITE_LOCKED;
+  StateLock slock{state_};
+  if (try_lock_(slock)) {
     return;
   }
   MutexWaiter waiter;
   write_waiters_.push_back(waiter);
-  ulock.unlock();
+  slock.state() |= kHasWriteWaiters;
+  slock.unlock();
   waiter.wait();
 }
 
@@ -319,15 +441,16 @@
 template <typename Clock, typename Duration>
 bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_until(
     const std::chrono::time_point<Clock, Duration>& deadline) {
-  std::unique_lock ulock{lock_};
-  if (state_ == State::UNLOCKED) {
-    verify_unlocked_properties();
-    state_ = State::WRITE_LOCKED;
+  StateLock slock{state_};
+  if (try_lock_(slock)) {
     return true;
   }
   MutexWaiter waiter;
-  write_waiters_.push_back(waiter);
-  ulock.unlock();
+  {
+    write_waiters_.push_back(waiter);
+    slock.state() |= kHasWriteWaiters;
+    slock.unlock();
+  }
 
   if (!waiter.try_wait_until(deadline)) {
     // We timed out. Two cases:
@@ -335,58 +458,54 @@
     // 2. We're not in the waiter list anymore. This could happen if the baton
     //    times out but the mutex is unlocked before we reach this code. In
     //    this case we'll pretend we got the lock on time.
-    std::unique_lock guard{lock_};
+    StateLock slock2{state_};
     if (waiter.hook.is_linked()) {
       write_waiters_.erase(write_waiters_.iterator_to(waiter));
-      guard.unlock();
+      if (write_waiters_.empty() && (slock2.state() & kHasWriteWaiters)) {
+        slock2.state() &= ~kHasWriteWaiters;
+      }
+      slock2.unlock();
       waiter.wake(); // Ensure that destruction doesn't block.
       return false;
     }
   }
-  assert(state_ == State::WRITE_LOCKED);
+  assert(state_.load(std::memory_order_relaxed) & kWriteLocked);
   return true;
 }
 
 template <bool ReaderPriority, typename BatonType>
 bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock() {
-  std::lock_guard guard{lock_};
-  if (state_ == State::UNLOCKED) {
-    verify_unlocked_properties();
-    state_ = State::WRITE_LOCKED;
-    return true;
-  }
-  return false;
+  StateLock slock{state_};
+  return try_lock_(slock);
 }
 
 template <bool ReaderPriority, typename BatonType>
 void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock() {
-  if (kIsDebug) {
-    std::unique_lock ulock{lock_};
-    assert(state_ == State::WRITE_LOCKED);
-  }
+  assert(state_.load(std::memory_order_relaxed) & kWriteLocked);
   unlock_();
 }
 
 template <bool ReaderPriority, typename BatonType>
 void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_() {
-  std::unique_lock guard{lock_};
-  assert(state_ != State::UNLOCKED);
+  StateLock slock{state_};
   assert(
-      (state_ == State::READ_LOCKED && readers_ > 0) ||
-      (state_ == State::WRITE_LOCKED && readers_ == 0));
-  if (state_ == State::READ_LOCKED) {
-    readers_ -= 1;
+      (slock.state() & kWriteLocked) ||
+      (!(slock.state() & kWriteLocked) &&
+       ((slock.state() >> kReadersShift) > 0)));
+  if ((slock.state() >> kReadersShift) > 0) {
+    slock.state() -= kReadersInc;
   }
 
   if (!read_waiters_.empty() && (ReaderPriority || write_waiters_.empty())) {
     assert(
-        state_ == State::WRITE_LOCKED && readers_ == 0 &&
+        (slock.state() & kWriteLocked) &&
+        ((slock.state() >> kReadersShift) == 0) &&
         "read waiters can only accumulate while write locked");
-    state_ = State::READ_LOCKED;
-    readers_ = read_waiters_.size();
+    slock.state() &= ~kWriteLocked;
+    slock.state() += read_waiters_.size() * kReadersInc;
 
     MutexWaiterList waiters_to_wake = std::move(read_waiters_);
-    guard.unlock();
+    slock.unlock();
 
     while (!waiters_to_wake.empty()) {
       MutexWaiter& to_wake = waiters_to_wake.front();
@@ -397,39 +516,45 @@
     return;
   }
 
-  if (readers_ == 0) {
+  if ((slock.state() >> kReadersShift) == 0) {
     if (!write_waiters_.empty()) {
       assert(read_waiters_.empty() || !ReaderPriority);
-      state_ = State::WRITE_LOCKED;
+      assert(slock.state() & kHasWriteWaiters);
+      slock.state() |= kWriteLocked;
 
       // Wake a single writer (after releasing the spin lock)
       MutexWaiter& to_wake = write_waiters_.front();
       write_waiters_.pop_front();
-      guard.unlock();
+      if (write_waiters_.empty()) {
+        slock.state() &= ~kHasWriteWaiters;
+      }
+      slock.unlock();
       to_wake.wake();
       return;
     }
 
-    verify_unlocked_properties();
-    state_ = State::UNLOCKED;
+    slock.state() &= ~kWriteLocked;
+    verify_unlocked_properties(slock);
     return;
   }
 
-  assert(state_ == State::READ_LOCKED);
+  assert((slock.state() >> kReadersShift) > 0);
 }
 
 template <bool ReaderPriority, typename BatonType>
 void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_and_lock_shared() {
-  std::unique_lock guard{lock_};
-  assert(state_ == State::WRITE_LOCKED && readers_ == 0);
-  state_ = State::READ_LOCKED;
-  readers_ += 1;
+  StateLock slock{state_};
+  assert(
+      (slock.state() & kWriteLocked) &&
+      ((slock.state() >> kReadersShift) == 0));
+  slock.state() &= ~kWriteLocked;
+  slock.state() += kReadersInc;
 
   if (!read_waiters_.empty()) {
-    readers_ += read_waiters_.size();
+    slock.state() += read_waiters_.size() * kReadersInc;
 
     MutexWaiterList waiters_to_wake = std::move(read_waiters_);
-    guard.unlock();
+    slock.unlock();
 
     while (!waiters_to_wake.empty()) {
       MutexWaiter& to_wake = waiters_to_wake.front();
diff --git a/folly/folly/fibers/TimedMutex.h b/folly/folly/fibers/TimedMutex.h
--- a/folly/folly/fibers/TimedMutex.h
+++ b/folly/folly/fibers/TimedMutex.h
@@ -207,28 +207,30 @@
   void unlock_and_lock_shared();
 
  private:
-  // invariants that must hold when the lock is not held by anyone
-  void verify_unlocked_properties() {
-    assert(readers_ == 0);
-    assert(read_waiters_.empty());
-    assert(write_waiters_.empty());
-  }
+  class StateLock;
 
-  bool shouldReadersWait() const;
+  void verify_unlocked_properties(StateLock& slock) const;
+  FOLLY_ALWAYS_INLINE bool shouldReadersWait(StateLock& slock) const;
+  FOLLY_ALWAYS_INLINE static bool shouldReadersWait(uint64_t state);
 
-  void unlock_();
+  FOLLY_ALWAYS_INLINE bool try_lock_shared_fast();
+  FOLLY_ALWAYS_INLINE bool try_unlock_shared_fast();
 
-  enum class State : uint8_t { UNLOCKED, READ_LOCKED, WRITE_LOCKED };
+  bool try_lock_(StateLock& slock);
+  void unlock_();
 
   using MutexWaiter = detail::MutexWaiter<BatonType>;
   using MutexWaiterList =
       folly::CountedIntrusiveList<MutexWaiter, &MutexWaiter::hook>;
 
-  folly::SpinLock lock_; //< lock protecting the internal state
-  // (state_, read_waiters_, etc.)
-  State state_ = State::UNLOCKED;
-
-  uint32_t readers_ = 0; //< Number of readers who have the lock
+  // State word layout:
+  // [... <#readers:32> <has write waiters:1> <write locked:1> <state locked:1>]
+  constexpr static uint64_t kStateLocked = uint64_t{1} << 0;
+  constexpr static uint64_t kWriteLocked = uint64_t{1} << 1;
+  constexpr static uint64_t kHasWriteWaiters = uint64_t{1} << 2;
+  constexpr static uint64_t kReadersShift = 3;
+  constexpr static uint64_t kReadersInc = uint64_t{1} << kReadersShift;
+  std::atomic<uint64_t> state_ = 0;
 
   MutexWaiterList write_waiters_; //< List of thread / fibers waiting for
   //  exclusive access
diff --git a/folly/folly/fibers/WhenN-inl.h b/folly/folly/fibers/WhenN-inl.h
--- a/folly/folly/fibers/WhenN-inl.h
+++ b/folly/folly/fibers/WhenN-inl.h
@@ -32,9 +32,8 @@
         invoke_result_t<
             typename std::iterator_traits<InputIterator>::value_type>>>>::type
 collectN(InputIterator first, InputIterator last, size_t n) {
-  typedef invoke_result_t<
-      typename std::iterator_traits<InputIterator>::value_type>
-      Result;
+  using Result =
+      invoke_result_t<typename std::iterator_traits<InputIterator>::value_type>;
   assert(n > 0);
   assert(std::distance(first, last) >= 0);
   assert(n <= static_cast<size_t>(std::distance(first, last)));
@@ -145,9 +144,8 @@
         invoke_result_t<
             typename std::iterator_traits<InputIterator>::value_type>>::
         type> inline collectAll(InputIterator first, InputIterator last) {
-  typedef invoke_result_t<
-      typename std::iterator_traits<InputIterator>::value_type>
-      Result;
+  using Result =
+      invoke_result_t<typename std::iterator_traits<InputIterator>::value_type>;
   size_t n = size_t(std::distance(first, last));
   std::vector<Result> results;
   std::vector<size_t> order(n);
diff --git a/folly/folly/fibers/async/Async.h b/folly/folly/fibers/async/Async.h
--- a/folly/folly/fibers/async/Async.h
+++ b/folly/folly/fibers/async/Async.h
@@ -87,7 +87,7 @@
 template <typename T>
 class [[nodiscard]] Async {
  public:
-  typedef T inner_type;
+  using inner_type = T;
 
   // General use constructor
   template <typename... Us>
@@ -118,7 +118,7 @@
 template <>
 class [[nodiscard]] Async<void> {
  public:
-  typedef void inner_type;
+  using inner_type = void;
 
   /* implicit */ Async() {}
   /* implicit */ Async(Unit) {}
diff --git a/folly/folly/fibers/async/Collect-inl.h b/folly/folly/fibers/async/Collect-inl.h
--- a/folly/folly/fibers/async/Collect-inl.h
+++ b/folly/folly/fibers/async/Collect-inl.h
@@ -146,9 +146,10 @@
         !std::is_same<ResultType, Async<void>>::value,
         async_inner_type_t<ResultType>>::
         type>> inline collectAll(InputIterator first, InputIterator last) {
-  return Async(folly::fibers::collectAll(
-      detail::await_iterator<InputIterator>(first),
-      detail::await_iterator<InputIterator>(last)));
+  return Async(
+      folly::fibers::collectAll(
+          detail::await_iterator<InputIterator>(first),
+          detail::await_iterator<InputIterator>(last)));
 }
 
 template <class InputIterator, typename FuncType, typename ResultType>
diff --git a/folly/folly/fibers/traits.h b/folly/folly/fibers/traits.h
--- a/folly/folly/fibers/traits.h
+++ b/folly/folly/fibers/traits.h
@@ -41,17 +41,17 @@
 
 template <typename Ret, typename T, typename First, typename... Args>
 struct ExtractFirstArg<Ret (T::*)(First, Args...)> {
-  typedef First type;
+  using type = First;
 };
 
 template <typename Ret, typename T, typename First, typename... Args>
 struct ExtractFirstArg<Ret (T::*)(First, Args...) const> {
-  typedef First type;
+  using type = First;
 };
 
 template <typename Ret, typename First, typename... Args>
 struct ExtractFirstArg<Ret(First, Args...)> {
-  typedef First type;
+  using type = First;
 };
 
 } // namespace detail
@@ -62,14 +62,14 @@
 /** Specialization for non-function-object callables */
 template <typename F>
 struct FirstArgOf<F, typename std::enable_if<!std::is_class<F>::value>::type> {
-  typedef typename detail::ExtractFirstArg<
-      typename std::remove_pointer<F>::type>::type type;
+  using type = typename detail::ExtractFirstArg<
+      typename std::remove_pointer<F>::type>::type;
 };
 
 /** Specialization for function objects */
 template <typename F>
 struct FirstArgOf<F, typename std::enable_if<std::is_class<F>::value>::type> {
-  typedef typename FirstArgOf<decltype(&F::operator())>::type type;
+  using type = typename FirstArgOf<decltype(&F::operator())>::type;
 };
 
 } // namespace fibers
diff --git a/folly/folly/functional/ApplyTuple.h b/folly/folly/functional/ApplyTuple.h
--- a/folly/folly/functional/ApplyTuple.h
+++ b/folly/folly/functional/ApplyTuple.h
@@ -45,8 +45,9 @@
 template <std::size_t I>
 struct invoke_get_fn {
   template <typename T>
-  constexpr auto operator()(T&& t) const noexcept(noexcept(
-      get<I>(static_cast<T&&>(t)))) -> decltype(get<I>(static_cast<T&&>(t))) {
+  constexpr auto operator()(T&& t) const
+      noexcept(noexcept(get<I>(static_cast<T&&>(t))))
+          -> decltype(get<I>(static_cast<T&&>(t))) {
     return get<I>(static_cast<T&&>(t));
   }
 };
diff --git a/folly/folly/functional/Invoke.h b/folly/folly/functional/Invoke.h
--- a/folly/folly/functional/Invoke.h
+++ b/folly/folly/functional/Invoke.h
@@ -63,7 +63,7 @@
     return static_cast<F&&>(f)(static_cast<A&&>(a)...);
   }
   template <typename M, typename C, typename... A>
-  FOLLY_ERASE constexpr auto operator()(M C::*f, A&&... a) const
+  FOLLY_ERASE constexpr auto operator()(M C::* f, A&&... a) const
       noexcept(noexcept(std::mem_fn(f)(static_cast<A&&>(a)...)))
           -> decltype(std::mem_fn(f)(static_cast<A&&>(a)...)) {
     return std::mem_fn(f)(static_cast<A&&>(a)...);
@@ -85,12 +85,13 @@
 //  Substitutes as void if that holds; otherwise fails a static-assert.
 struct ok_one_ {
   template <typename T>
-  static constexpr bool pass_v = ( //
-      std::is_void_v<T> || //
-      std::is_reference_v<T> || //
-      std::is_function_v<T> || //
-      is_unbounded_array_v<T> || //
-      false);
+  static constexpr bool pass_v =
+      ( //
+          std::is_void_v<T> || //
+          std::is_reference_v<T> || //
+          std::is_function_v<T> || //
+          is_unbounded_array_v<T> || //
+          false);
 
   // note: void return type with no function body to enforce that, in the
   // typical case of complete non-function types, to minimize the quantity of
@@ -161,15 +162,15 @@
 template <typename M, typename C>
 struct traits<M C::*> : traits_member_ptr<M C::*> {};
 template <typename M, typename C>
-struct traits<M C::*const> : traits_member_ptr<M C::*> {};
+struct traits<M C::* const> : traits_member_ptr<M C::*> {};
 template <typename M, typename C>
 struct traits<M C::*&> : traits_member_ptr<M C::*> {};
 template <typename M, typename C>
-struct traits<M C::*const&> : traits_member_ptr<M C::*> {};
+struct traits<M C::* const&> : traits_member_ptr<M C::*> {};
 template <typename M, typename C>
 struct traits<M C::*&&> : traits_member_ptr<M C::*> {};
 template <typename M, typename C>
-struct traits<M C::*const&&> : traits_member_ptr<M C::*> {};
+struct traits<M C::* const&&> : traits_member_ptr<M C::*> {};
 
 #if defined(_MSC_VER) && defined(__NVCC__)
 template <typename P, typename... A>
@@ -761,6 +762,33 @@
   template <typename T>                                            \
   [[maybe_unused]] inline constexpr membername##_fn<T> membername {}
 
+/***
+ *  FOLLY_CREATE_MEMBER_ACCESSOR
+ *
+ *  Used to create an accessor type bound to a specific data-member name,
+ *  providing access to that data-member.
+ */
+#define FOLLY_CREATE_MEMBER_ACCESSOR(classname, membername)            \
+  struct classname {                                                   \
+    template <typename T>                                              \
+    [[maybe_unused]] FOLLY_ERASE_HACK_GCC constexpr auto&& operator()( \
+        T&& val) const noexcept {                                      \
+      return static_cast<T&&>(val).membername;                         \
+    }                                                                  \
+  }
+
+/***
+ *  FOLLY_CREATE_MEMBER_ACCESSOR_SUITE
+ *
+ *  Used to create an accessor type and associated variable bound to a specific
+ *  data-member name, providing access to that data-member. The accessor
+ *  variable is named like the member name and the accessor type is named with a
+ *  suffix of _fn.
+ */
+#define FOLLY_CREATE_MEMBER_ACCESSOR_SUITE(membername)       \
+  FOLLY_CREATE_MEMBER_ACCESSOR(membername##_fn, membername); \
+  [[maybe_unused]] inline constexpr membername##_fn membername {}
+
 namespace folly {
 
 namespace detail_tag_invoke_fn {
@@ -893,5 +921,68 @@
           is_tag_invocable_v<Tag, Args...>,
           detail_tag_invoke_fn::defer<tag_invoke_result_t, Tag, Args...>,
           detail_tag_invoke_fn::empty> {};
+
+#if defined(__cpp_concepts)
+
+/// passable_to
+///
+/// Useful for enabling n^k overloads all at once. Example below shows cases of
+/// n=5 with k=1 and k=2.
+///
+/// Useful for transparent hash and key-equal functions for a set which needs
+/// heterogeneous lookup.
+///
+/// Law:
+///   passable_to<Arg, Fun> = invocable<Fun, Arg>
+///
+/// Discussion:
+/// Q:  Why require a one-param function type and not support a multi-param
+///     function type?
+/// A:  Too much arbitrary choice. Interface would be confusing.
+///     To provide an interface like:
+///         passable_to<Slog, Fun, Arg...>
+///     Then `Arg...` would need exactly one "hole", which would be represented
+///     by perhaps `void` or perhaps `std::placeholder::_1` or another sentinel.
+///     Alternatively:
+///         passable_to<Slog, Fun, tag_t<ArgFront...>, tag_t<ArgBack...>>
+///     Now the usage gets convoluted.
+///     But, in the end, this can be worked around by passing a function type
+///     that is the result of std::bind_front (C++20) or std::bind_back (C++23).
+///
+/// Example:
+///
+///     struct to_key_fn {
+///       string_view operator()(string_view val) const noexcept { return val; }
+///       string_view operator()(object const&) const noexcept;
+///       string_view operator()(object const*) const noexcept;
+///       string_view operator()(unique_ptr<object> const&) const noexcept;
+///       string_view operator()(shared_ptr<object> const&) const noexcept;
+///     };
+///     static constexpr to_key_fn to_key{};
+///
+///     struct obj_hash : hash<string_view> {
+///       using is_transparent = void;
+///       size_t operator()(passable_to<to_key_fn> auto const& val) noexcept {
+///         return hash<string_view>::operator()(to_key(val));
+///       }
+///     };
+///     struct obj_key_equal : equal_to<string_view> {
+///       using is_transparent = void;
+///       bool operator()(
+///           passable_to<to_key_fn> auto const& lhs,
+///           passable_to<to_key_fn> auto const& rhs) noexcept {
+///         return equal_to<string_view>::operator()(to_key(lhs), to_key(rhs));
+///       }
+///     };
+///
+///     using object_set = std::unordered_set<
+///         std::shared_ptr<object>,
+///         obj_hash,
+///         obj_key_equal>;
+///     object_set objs;
+template <typename Arg, typename Fun>
+concept passable_to = is_invocable_v<Fun, Arg>;
+
+#endif
 
 } // namespace folly
diff --git a/folly/folly/functional/Partial.h b/folly/folly/functional/Partial.h
--- a/folly/folly/functional/Partial.h
+++ b/folly/folly/functional/Partial.h
@@ -57,32 +57,22 @@
 
   template <typename... CArgs>
   auto operator()(CArgs&&... cargs) & -> decltype(invokeForward(
-                                          std::declval<Partial&>(),
-                                          Indexes{},
-                                          std::declval<CArgs>()...)) {
+      std::declval<Partial&>(), Indexes{}, std::declval<CArgs>()...)) {
     return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...);
   }
   template <typename... CArgs>
-  auto operator()(CArgs&&... cargs)
-      const& -> decltype(invokeForward(
-                 std::declval<const Partial&>(),
-                 Indexes{},
-                 std::declval<CArgs>()...)) {
+  auto operator()(CArgs&&... cargs) const& -> decltype(invokeForward(
+      std::declval<const Partial&>(), Indexes{}, std::declval<CArgs>()...)) {
     return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...);
   }
   template <typename... As>
   auto operator()(As&&... a) && -> decltype(invokeForward(
-                                    std::declval<Partial&&>(),
-                                    Indexes{},
-                                    std::declval<As>()...)) {
+      std::declval<Partial&&>(), Indexes{}, std::declval<As>()...)) {
     return invokeForward(std::move(*this), Indexes{}, std::forward<As>(a)...);
   }
   template <typename... As>
-  auto operator()(As&&... as)
-      const&& -> decltype(invokeForward(
-                  std::declval<const Partial&&>(),
-                  Indexes{},
-                  std::declval<As>()...)) {
+  auto operator()(As&&... as) const&& -> decltype(invokeForward(
+      std::declval<const Partial&&>(), Indexes{}, std::declval<As>()...)) {
     return invokeForward(std::move(*this), Indexes{}, std::forward<As>(as)...);
   }
 
diff --git a/folly/folly/functional/protocol.h b/folly/folly/functional/protocol.h
--- a/folly/folly/functional/protocol.h
+++ b/folly/folly/functional/protocol.h
@@ -95,10 +95,11 @@
 //  * std::hash
 //  * std::default_delete
 template <typename F>
-static constexpr bool match_static_lambda_protocol_v = ( //
-    std::is_empty<F>::value && //
-    std::is_trivially_copyable_v<F> && //
-    true);
+static constexpr bool match_static_lambda_protocol_v =
+    ( //
+        std::is_empty<F>::value && //
+        std::is_trivially_copyable_v<F> && //
+        true);
 
 //  ----
 
diff --git a/folly/folly/functional/traits.h b/folly/folly/functional/traits.h
--- a/folly/folly/functional/traits.h
+++ b/folly/folly/functional/traits.h
@@ -140,7 +140,7 @@
       detail::function_traits_cvref_<int const volatile> {};
 
 template <typename R, typename... A>
-struct function_traits<R(A...)&> //
+struct function_traits<R(A...) &> //
     : detail::function_traits_base_<R(A...)>,
       detail::function_traits_nx_<false>,
       detail::function_traits_var_<false>,
@@ -224,7 +224,7 @@
       detail::function_traits_cvref_<int const volatile> {};
 
 template <typename R, typename... A>
-struct function_traits<R(A..., ...)&> //
+struct function_traits<R(A..., ...) &> //
     : detail::function_traits_base_<R(A...)>,
       detail::function_traits_nx_<false>,
       detail::function_traits_var_<true>,
diff --git a/folly/folly/futures/Barrier.h b/folly/folly/futures/Barrier.h
--- a/folly/folly/futures/Barrier.h
+++ b/folly/folly/futures/Barrier.h
@@ -50,7 +50,7 @@
   folly::Future<bool> wait();
 
  private:
-  typedef folly::Promise<bool> BoolPromise;
+  using BoolPromise = folly::Promise<bool>;
 
   static constexpr uint64_t kReaderShift = 32;
   static constexpr uint64_t kReader = uint64_t(1) << kReaderShift;
diff --git a/folly/folly/futures/Future-inl.h b/folly/folly/futures/Future-inl.h
--- a/folly/folly/futures/Future-inl.h
+++ b/folly/folly/futures/Future-inl.h
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <chrono>
+#include <deque>
 #include <thread>
 #include <type_traits>
 #include <utility>
@@ -539,7 +540,7 @@
   }
 
   struct Queue {
-    std::vector<Func> funcs;
+    std::deque<Func> funcs;
     bool detached{false};
   };
 
@@ -609,9 +610,16 @@
 }
 
 template <class T, class E>
-typename std::
-    enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type
-    makeSemiFuture(E const& e) {
+std::enable_if_t<std::is_base_of_v<std::exception, decay_t<E>>, SemiFuture<T>>
+makeSemiFuture(E&& e) {
+  return makeSemiFuture(Try<T>(make_exception_wrapper<E>(std::forward<E>(e))));
+}
+
+// DEPRECATED const-ref overload for users who EXPLICITLY specify BOTH template
+// parameters
+template <class T, class E>
+std::enable_if_t<std::is_base_of_v<std::exception, E>, SemiFuture<T>>
+makeSemiFuture(const folly::type_identity_t<E>& e) {
   return makeSemiFuture(Try<T>(make_exception_wrapper<E>(e)));
 }
 
@@ -822,7 +830,7 @@
 SemiFuture<T> SemiFuture<T>::deferError(tag_t<ExceptionType>, F&& func) && {
   return std::move(*this).defer(
       [func_2 = static_cast<F&&>(func)](Try<T>&& t) mutable {
-        if (auto e = t.template tryGetExceptionObject<ExceptionType>()) {
+        if (const auto* e = t.template tryGetExceptionObject<ExceptionType>()) {
           return makeSemiFutureWith([&]() mutable {
             return static_cast<F&&>(func_2)(*e);
           });
@@ -1118,9 +1126,9 @@
       [state = futures::detail::makeCoreCallbackState(
            std::move(p), static_cast<F&&>(func)),
        allowInline](Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {
-        if (auto ex = t.template tryGetExceptionObject<
-                      std::remove_reference_t<ExceptionType>>()) {
-          auto tf2 = state.tryInvoke(std::move(*ex));
+        if (const auto* ex = t.template tryGetExceptionObject<
+                             std::remove_reference_t<ExceptionType>>()) {
+          auto tf2 = state.tryInvoke(*ex);
           if (tf2.hasException()) {
             state.setException(std::move(ka), std::move(tf2.exception()));
           } else {
@@ -1169,11 +1177,10 @@
       [state = futures::detail::makeCoreCallbackState(
            std::move(p), static_cast<F&&>(func))](
           Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {
-        if (auto ex = t.template tryGetExceptionObject<
-                      std::remove_reference_t<ExceptionType>>()) {
+        if (const auto* ex = t.template tryGetExceptionObject<
+                             std::remove_reference_t<ExceptionType>>()) {
           state.setTry(
-              std::move(ka),
-              makeTryWith([&] { return state.invoke(std::move(*ex)); }));
+              std::move(ka), makeTryWith([&] { return state.invoke(*ex); }));
         } else {
           state.setTry(std::move(ka), std::move(t));
         }
@@ -1370,9 +1377,16 @@
 }
 
 template <class T, class E>
-typename std::enable_if<std::is_base_of<std::exception, E>::value, Future<T>>::
-    type
-    makeFuture(E const& e) {
+std::enable_if_t<std::is_base_of_v<std::exception, decay_t<E>>, Future<T>>
+makeFuture(E&& e) {
+  return makeFuture(Try<T>(make_exception_wrapper<E>(std::forward<E>(e))));
+}
+
+// DEPRECATED const-ref overload for users who EXPLICITLY specify BOTH template
+// parameters
+template <class T, class E>
+std::enable_if_t<std::is_base_of_v<std::exception, E>, Future<T>> makeFuture(
+    const folly::type_identity_t<E>& e) {
   return makeFuture(Try<T>(make_exception_wrapper<E>(e)));
 }
 
@@ -2182,8 +2196,9 @@
       tk->after(dur).defer(futures::detail::WithinAfterFutureCallback{ctx});
 
   // Properly propagate interrupt values through futures chained after within()
-  ctx->promise.setInterruptHandler(futures::detail::WithinInterruptHandler{
-      to_weak_ptr_aliasing(ctx, ctx->thisFuture.core_)});
+  ctx->promise.setInterruptHandler(
+      futures::detail::WithinInterruptHandler{
+          to_weak_ptr_aliasing(ctx, ctx->thisFuture.core_)});
 
   // Construct the future to return, create a fresh DeferredExecutor and
   // nest the other two inside it, in case they already carry nested executors.
@@ -2268,9 +2283,9 @@
   Promise<T> promise;
   auto ret = convertFuture(promise.getSemiFuture(), f);
   auto baton = std::make_shared<FutureBatonType>();
-  f.setCallback_([baton, promise = std::move(promise)](
+  f.setCallback_([baton, promise_2 = std::move(promise)](
                      Executor::KeepAlive<>&&, Try<T>&& t) mutable {
-    promise.setTry(std::move(t));
+    promise_2.setTry(std::move(t));
     baton->post();
   });
   f = std::move(ret);
@@ -2565,9 +2580,10 @@
   if (predicate()) {
     auto future = thunk();
     return std::move(future).deferExValue(
-        [predicate = static_cast<P&&>(predicate),
-         thunk = static_cast<F&&>(thunk)](auto&& ex, auto&&) mutable {
-          return whileDo(static_cast<P&&>(predicate), static_cast<F&&>(thunk))
+        [predicate_2 = static_cast<P&&>(predicate),
+         thunk_2 = static_cast<F&&>(thunk)](auto&& ex, auto&&) mutable {
+          return whileDo(
+                     static_cast<P&&>(predicate_2), static_cast<F&&>(thunk_2))
               .via(std::move(ex));
         });
   }
@@ -2580,9 +2596,10 @@
   if (predicate()) {
     auto future = thunk();
     return std::move(future).thenValue(
-        [predicate = static_cast<P&&>(predicate),
-         thunk = static_cast<F&&>(thunk)](auto&&) mutable {
-          return whileDo(static_cast<P&&>(predicate), static_cast<F&&>(thunk));
+        [predicate_2 = static_cast<P&&>(predicate),
+         thunk_2 = static_cast<F&&>(thunk)](auto&&) mutable {
+          return whileDo(
+              static_cast<P&&>(predicate_2), static_cast<F&&>(thunk_2));
         });
   }
   return makeFuture();
@@ -2643,11 +2660,12 @@
 template <typename F, class Ensure>
 auto ensure(F&& f, Ensure&& ensure) {
   return makeSemiFuture()
-      .deferValue([f = static_cast<F&&>(f)](auto) mutable { return f(); })
-      .defer([ensure = static_cast<Ensure&&>(ensure)](auto resultTry) mutable {
-        ensure();
-        return std::move(resultTry).value();
-      });
+      .deferValue([f_2 = static_cast<F&&>(f)](auto) mutable { return f_2(); })
+      .defer(
+          [ensure_2 = static_cast<Ensure&&>(ensure)](auto resultTry) mutable {
+            ensure_2();
+            return std::move(resultTry).value();
+          });
 }
 
 template <class T>
@@ -2663,11 +2681,12 @@
 template <class T>
 void maybeDetachOnGlobalExecutorAfter(
     HighResDuration dur, folly::SemiFuture<T>&& fut) {
-  sleep(dur).toUnsafeFuture().thenValue([fut = std::move(fut)](auto&&) mutable {
-    if (auto ptr = folly::detail::tryGetImmutableCPUPtr()) {
-      detachOn(folly::getKeepAliveToken(ptr.get()), std::move(fut));
-    }
-  });
+  sleep(dur).toUnsafeFuture().thenValue(
+      [fut_2 = std::move(fut)](auto&&) mutable {
+        if (auto ptr = folly::detail::tryGetImmutableCPUPtr()) {
+          detachOn(folly::getKeepAliveToken(ptr.get()), std::move(fut_2));
+        }
+      });
 }
 
 template <class T>
diff --git a/folly/folly/futures/Future.cpp b/folly/folly/futures/Future.cpp
--- a/folly/folly/futures/Future.cpp
+++ b/folly/folly/futures/Future.cpp
@@ -117,19 +117,35 @@
 
 namespace detail {
 
+FOLLY_EXPORT FOLLY_ALWAYS_INLINE
+    folly::Singleton<Timekeeper, TimekeeperSingletonTag>&
+    getOrCreateTimekeeperSingleton() {
+  using TimekeeperSingleton =
+      folly::Singleton<Timekeeper, TimekeeperSingletonTag>;
+  static TimekeeperSingleton sTimekeeperSingleton([]() -> Timekeeper* {
+    if (FLAGS_folly_futures_use_thread_wheel_timekeeper) {
+      return new ThreadWheelTimekeeper;
+    } else {
+      return new HeapTimekeeper;
+    }
+  });
+  return sTimekeeperSingleton;
+}
+
 namespace {
-Singleton<Timekeeper, TimekeeperSingletonTag> gTimekeeperSingleton(
-    []() -> Timekeeper* {
-      if (FLAGS_folly_futures_use_thread_wheel_timekeeper) {
-        return new ThreadWheelTimekeeper;
-      } else {
-        return new HeapTimekeeper;
-      }
-    });
+class TimekeeperSingletonInstantiator {
+ public:
+  TimekeeperSingletonInstantiator() {
+    // call this function to force the timekeeper singleton to be created
+    folly::detail::getOrCreateTimekeeperSingleton();
+  }
+};
+TimekeeperSingletonInstantiator gTimekeeperSingletonInstantiator;
 } // namespace
 
 std::shared_ptr<Timekeeper> getTimekeeperSingleton() {
-  return gTimekeeperSingleton.try_get();
+  (void)gTimekeeperSingletonInstantiator;
+  return getOrCreateTimekeeperSingleton().try_get();
 }
 
 } // namespace detail
diff --git a/folly/folly/futures/Future.h b/folly/folly/futures/Future.h
--- a/folly/folly/futures/Future.h
+++ b/folly/folly/futures/Future.h
@@ -143,7 +143,7 @@
   using Core = futures::detail::Core<T>;
 
  public:
-  typedef T value_type;
+  using value_type = T;
 
   /// Construct from a value (perfect forwarding)
   ///
@@ -365,8 +365,9 @@
   /// See `raise(exception_wrapper)` for details.
   template <class E>
   void raise(E&& exception) {
-    raise(make_exception_wrapper<typename std::remove_reference<E>::type>(
-        static_cast<E&&>(exception)));
+    raise(
+        make_exception_wrapper<typename std::remove_reference<E>::type>(
+            static_cast<E&&>(exception)));
   }
 
   /// Raises a FutureCancellation interrupt.
@@ -2233,10 +2234,20 @@
 /** Make a SemiFuture from an exception type E that can be passed to
   std::make_exception_ptr(). */
 template <class T, class E>
-typename std::
-    enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type
-    makeSemiFuture(E const& e);
+std::enable_if_t<std::is_base_of_v<std::exception, decay_t<E>>, SemiFuture<T>>
+makeSemiFuture(E&& e);
 
+/** Make a SemiFuture from an exception type E that can be passed to
+  std::make_exception_ptr().
+  NOTE: This is a deprecated const-ref overload for users who explicitly specify
+  both template parameters. Please leave exception type deduction to the
+  compiler.
+ */
+template <class T, class E>
+[[deprecated("do not specify exception type template parameter explicitly")]]
+std::enable_if_t<std::is_base_of_v<std::exception, E>, SemiFuture<T>>
+makeSemiFuture(const folly::type_identity_t<E>& e);
+
 /** Make a Future out of a Try */
 template <class T>
 SemiFuture<T> makeSemiFuture(Try<T> t);
@@ -2325,10 +2336,20 @@
        valid Future where necessary.
  */
 template <class T, class E>
-typename std::enable_if<std::is_base_of<std::exception, E>::value, Future<T>>::
-    type
-    makeFuture(E const& e);
+std::enable_if_t<std::is_base_of_v<std::exception, decay_t<E>>, Future<T>>
+makeFuture(E&& e);
 
+/** Make a Future from an exception type E that can be passed to
+  std::make_exception_ptr().
+  NOTE: This is a deprecated const-ref overload for users who explicitly specify
+  both template parameters. Please leave exception type deduction to the
+  compiler.
+ */
+template <class T, class E>
+[[deprecated("do not specify exception type template parameter explicitly")]]
+std::enable_if_t<std::is_base_of_v<std::exception, E>, Future<T>> makeFuture(
+    const folly::type_identity_t<E>& e);
+
 /**
   Make a Future out of a Try
 
@@ -2551,12 +2572,8 @@
 
 /// Sugar for the most common case
 template <class Collection, class T, class F>
-auto reduce(Collection&& c, T&& initial, F&& func)
-    -> decltype(folly::reduce(
-        c.begin(),
-        c.end(),
-        static_cast<T&&>(initial),
-        static_cast<F&&>(func))) {
+auto reduce(Collection&& c, T&& initial, F&& func) -> decltype(folly::reduce(
+    c.begin(), c.end(), static_cast<T&&>(initial), static_cast<F&&>(func))) {
   return folly::reduce(
       c.begin(), c.end(), static_cast<T&&>(initial), static_cast<F&&>(func));
 }
@@ -2616,7 +2633,7 @@
   explicit FutureAwaiter(folly::Future<T>&& future) noexcept
       : future_(std::move(future)) {}
 
-  bool await_ready() {
+  bool await_ready() noexcept {
     if (future_.isReady()) {
       result_ = std::move(future_.result());
       return true;
@@ -2630,17 +2647,38 @@
     return static_cast<Try<drop_unit_t<T>>>(std::move(result_));
   }
 
-  FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend(
-      coro::coroutine_handle<> h) {
+  // Precondition: The Future must not already have a callback, or
+  // `setCallback_` will throw `FutureAlreadyContinued` when resumed.
+  FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES bool await_suspend(
+      coro::coroutine_handle<> h) noexcept {
     // FutureAwaiter may get destroyed as soon as the callback is executed.
     // Make sure the future object doesn't get destroyed until setCallback_
     // returns.
     auto future = std::move(future_);
-    future.setCallback_(
-        [this, h](Executor::KeepAlive<>&&, Try<T>&& result) mutable {
-          result_ = std::move(result);
-          h.resume();
-        });
+    try {
+      future.setCallback_(
+          [this, h](Executor::KeepAlive<>&&, Try<T>&& result) mutable {
+            result_ = std::move(result);
+            h.resume();
+          });
+      return true; // Suspend: callback will resume later
+    } catch (...) {
+      // Before `await_suspend` was made noexcept, FutureAlreadyContinued
+      // could escape. Production code may rely on this, so we forward the
+      // error to `await_resume` instead of crashing.
+      //
+      // Only FutureAlreadyContinued should be catchable here:
+      // - FutureInvalid can't happen because co_viaIfAsync (noexcept) would
+      //   have already terminated if the future was invalid.
+      // - In debug builds, the DCHECK in Core::setExecutor fires inside
+      //   co_viaIfAsync if a callback was already attached.
+      // - In opt builds, we reach here and catch FutureAlreadyContinued.
+      DCHECK(
+          current_exception_wrapper()
+              .is_compatible_with<FutureAlreadyContinued>());
+      result_ = Try<T>(current_exception_wrapper());
+      return false; // Don't suspend: resume immediately with error
+    }
   }
 
  private:
diff --git a/folly/folly/futures/HeapTimekeeper.cpp b/folly/folly/futures/HeapTimekeeper.cpp
--- a/folly/folly/futures/HeapTimekeeper.cpp
+++ b/folly/folly/futures/HeapTimekeeper.cpp
@@ -300,7 +300,7 @@
 }
 
 HeapTimekeeper::HeapTimekeeper() : state_(std::make_shared<State>()) {
-  thread_ = std::thread{[this] { state_->worker(); }};
+  thread_ = std::thread{[this] { worker(); }};
 }
 
 HeapTimekeeper::~HeapTimekeeper() {
@@ -312,6 +312,10 @@
   auto [timeout, sf] = Timeout::create(*this, Clock::now() + dur);
   state_->enqueue(State::Op::Type::kSchedule, std::move(timeout));
   return std::move(sf);
+}
+
+void HeapTimekeeper::worker() const {
+  state_->worker();
 }
 
 } // namespace folly
diff --git a/folly/folly/futures/HeapTimekeeper.h b/folly/folly/futures/HeapTimekeeper.h
--- a/folly/folly/futures/HeapTimekeeper.h
+++ b/folly/folly/futures/HeapTimekeeper.h
@@ -36,6 +36,9 @@
 
   SemiFuture<Unit> after(HighResDuration) override;
 
+ protected:
+  virtual void worker() const;
+
  private:
   using Clock = std::chrono::steady_clock;
 
diff --git a/folly/folly/futures/Portability.h b/folly/folly/futures/Portability.h
--- a/folly/folly/futures/Portability.h
+++ b/folly/folly/futures/Portability.h
@@ -16,4 +16,4 @@
 
 #pragma once
 
-#include <folly/Portability.h>
+#include <folly/Portability.h> // @shim
diff --git a/folly/folly/futures/Retrying.h b/folly/folly/futures/Retrying.h
--- a/folly/folly/futures/Retrying.h
+++ b/folly/folly/futures/Retrying.h
@@ -76,7 +76,7 @@
 Future<typename isFutureOrSemiFuture<invoke_result_t<FF, size_t>>::Inner>
 retryingUnsafe(Policy&& p, FF&& ff);
 template <class Policy, class FF>
-FOLLY_NODISCARD SemiFuture<
+[[nodiscard]] SemiFuture<
     typename isFutureOrSemiFuture<invoke_result_t<FF, size_t>>::Inner>
 retrying(Policy&& p, FF&& ff);
 
diff --git a/folly/folly/futures/WTCallback.h b/folly/folly/futures/WTCallback.h
--- a/folly/folly/futures/WTCallback.h
+++ b/folly/folly/futures/WTCallback.h
@@ -80,11 +80,11 @@
     }
 
     auto state = std::exchange(*wState, {});
-    auto* base = state->base;
-    base->runInEventBaseThreadAlwaysEnqueue(
-        [self, state = std::move(state), ew = std::move(ew)]() mutable {
+    auto* base_2 = state->base;
+    base_2->runInEventBaseThreadAlwaysEnqueue(
+        [self, state_2 = std::move(state), ew_2 = std::move(ew)]() mutable {
           self->cancelTimeout();
-          state->promise.setException(std::move(ew));
+          state_2->promise.setException(std::move(ew_2));
         });
   }
 };
diff --git a/folly/folly/futures/detail/Core.h b/folly/folly/futures/detail/Core.h
--- a/folly/folly/futures/detail/Core.h
+++ b/folly/folly/futures/detail/Core.h
@@ -34,6 +34,7 @@
 #include <folly/io/async/Request.h>
 #include <folly/lang/Assume.h>
 #include <folly/lang/Exception.h>
+#include <folly/lang/Switch.h>
 #include <folly/synchronization/AtomicUtil.h>
 
 namespace folly {
@@ -437,50 +438,55 @@
     }
     handler_type* handler = nullptr;
     auto interrupt = interrupt_.load(std::memory_order_acquire);
-    switch (interrupt & InterruptMask) {
-      case InterruptInitial: { // store the handler
-        assert(!interrupt);
-        handler = new handler_type(static_cast<F&&>(fn));
-        auto exchanged = folly::atomic_compare_exchange_strong_explicit(
-            &interrupt_,
-            &interrupt,
-            reinterpret_cast<uintptr_t>(handler) | InterruptHasHandler,
-            std::memory_order_release,
-            std::memory_order_acquire);
-        if (exchanged) {
-          return;
-        }
-        // lost the race!
-        if (interrupt & InterruptHasHandler) {
-          terminate_with<std::logic_error>("set-interrupt-handler race");
-        }
-        assert(interrupt & InterruptHasObject);
-        [[fallthrough]];
-      }
-      case InterruptHasObject: { // invoke over the stored object
-        auto exchanged = interrupt_.compare_exchange_strong(
-            interrupt, InterruptTerminal, std::memory_order_relaxed);
-        if (!exchanged) {
-          terminate_with<std::logic_error>("set-interrupt-handler race");
+    FOLLY_EXHAUSTIVE_SWITCH({
+      switch (interrupt & InterruptMask) {
+        case InterruptInitial: { // store the handler
+          assert(!interrupt);
+          handler = new handler_type(static_cast<F&&>(fn));
+          auto exchanged = folly::atomic_compare_exchange_strong_explicit(
+              &interrupt_,
+              &interrupt,
+              reinterpret_cast<uintptr_t>(handler) | InterruptHasHandler,
+              std::memory_order_release,
+              std::memory_order_acquire);
+          if (exchanged) {
+            return;
+          }
+          // lost the race!
+          if (interrupt & InterruptHasHandler) {
+            terminate_with<std::logic_error>("set-interrupt-handler race");
+          }
+          assert(interrupt & InterruptHasObject);
+          [[fallthrough]];
         }
-        auto pointer = interrupt & ~InterruptMask;
-        auto object = reinterpret_cast<exception_wrapper*>(pointer);
-        if (handler) {
-          handler->handle(*object);
-          delete handler;
-        } else {
-          // mimic constructing and invoking a handler: 1 copy; non-const invoke
-          auto fn_ = static_cast<F&&>(fn);
-          fn_(std::as_const(*object));
+        case InterruptHasObject: { // invoke over the stored object
+          auto exchanged = interrupt_.compare_exchange_strong(
+              interrupt, InterruptTerminal, std::memory_order_relaxed);
+          if (!exchanged) {
+            terminate_with<std::logic_error>("set-interrupt-handler race");
+          }
+          auto pointer = interrupt & ~InterruptMask;
+          auto object = reinterpret_cast<exception_wrapper*>(pointer);
+          if (handler) {
+            handler->handle(*object);
+            delete handler;
+          } else {
+            // mimic constructing and invoking a handler: 1 copy; non-const
+            // invoke
+            auto fn_ = static_cast<F&&>(fn);
+            fn_(std::as_const(*object));
+          }
+          delete object;
+          return;
         }
-        delete object;
-        return;
+        case InterruptHasHandler: // fail all calls after the first
+          terminate_with<std::logic_error>("set-interrupt-handler duplicate");
+        case InterruptTerminal: // fail all calls after the first
+          terminate_with<std::logic_error>("set-interrupt-handler after done");
+        default:
+          folly::assume_unreachable();
       }
-      case InterruptHasHandler: // fail all calls after the first
-        terminate_with<std::logic_error>("set-interrupt-handler duplicate");
-      case InterruptTerminal: // fail all calls after the first
-        terminate_with<std::logic_error>("set-interrupt-handler after done");
-    }
+    }); // FOLLY_EXHAUSTIVE_SWITCH
   }
 
  protected:
@@ -628,11 +634,11 @@
       std::shared_ptr<folly::RequestContext>&& context,
       futures::detail::InlineContinuation allowInline) {
     Callback callback =
-        [func = static_cast<F&&>(func)](
+        [func_2 = static_cast<F&&>(func)](
             CoreBase& coreBase,
             Executor::KeepAlive<>&& ka,
             exception_wrapper* ew) mutable {
-          func(std::move(ka), setCallbackGetResult(coreBase, ew));
+          func_2(std::move(ka), setCallbackGetResult(coreBase, ew));
         };
 
     setCallback_(std::move(callback), std::move(context), allowInline);
diff --git a/folly/folly/gen/Base-inl.h b/folly/folly/gen/Base-inl.h
--- a/folly/folly/gen/Base-inl.h
+++ b/folly/folly/gen/Base-inl.h
@@ -2311,10 +2311,8 @@
  *   set<string> uniqueNames = from(names) | as<set>();
  */
 template <
-    template <class, class>
-    class Container,
-    template <class>
-    class Allocator>
+    template <class, class> class Container,
+    template <class> class Allocator>
 class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {
  public:
   CollectTemplate() = default;
diff --git a/folly/folly/gen/Base.h b/folly/folly/gen/Base.h
--- a/folly/folly/gen/Base.h
+++ b/folly/folly/gen/Base.h
@@ -112,7 +112,7 @@
 template <class Class, class Result>
 class MemberFunction {
  public:
-  typedef Result (Class::*MemberPtr)();
+  using MemberPtr = Result (Class::*)();
 
  private:
   MemberPtr member_;
@@ -130,7 +130,7 @@
 template <class Class, class Result>
 class ConstMemberFunction {
  public:
-  typedef Result (Class::*MemberPtr)() const;
+  using MemberPtr = Result (Class::*)() const;
 
  private:
   MemberPtr member_;
@@ -146,7 +146,7 @@
 template <class Class, class FieldType>
 class Field {
  public:
-  typedef FieldType Class::*FieldPtr;
+  using FieldPtr = FieldType Class::*;
 
  private:
   FieldPtr field_;
@@ -496,6 +496,8 @@
  */
 #define GENERATOR(TYPE) \
   ::folly::gen::detail::GeneratorBuilder<TYPE>() + [=](auto&& yield)
+#define GENERATOR_WITH_THIS(TYPE) \
+  ::folly::gen::detail::GeneratorBuilder<TYPE>() + [ =, this ](auto&& yield)
 #define GENERATOR_REF(TYPE) \
   ::folly::gen::detail::GeneratorBuilder<TYPE>() + [&](auto&& yield)
 
@@ -622,7 +624,7 @@
     class FieldType,
     class Field = Field<Class, FieldType>,
     class Map = detail::Map<Field>>
-Map field(FieldType Class::*field) {
+Map field(FieldType Class::* field) {
   return Map(Field(field));
 }
 
diff --git a/folly/folly/gen/Combine-inl.h b/folly/folly/gen/Combine-inl.h
--- a/folly/folly/gen/Combine-inl.h
+++ b/folly/folly/gen/Combine-inl.h
@@ -120,8 +120,9 @@
         if (iter == container_->end()) {
           return false;
         }
-        if (!handler(std::make_tuple(
-                std::forward<Value>(value), std::move(*iter)))) {
+        if (!handler(
+                std::make_tuple(
+                    std::forward<Value>(value), std::move(*iter)))) {
           return false;
         }
         ++iter;
@@ -164,9 +165,8 @@
 }
 
 template <class Type1, class Type2>
-auto add_to_tuple(Type1&& t1, Type2&& t2)
-    -> decltype(std::make_tuple(
-        std::forward<Type1>(t1), std::forward<Type2>(t2))) {
+auto add_to_tuple(Type1&& t1, Type2&& t2) -> decltype(std::make_tuple(
+    std::forward<Type1>(t1), std::forward<Type2>(t2))) {
   return std::make_tuple(std::forward<Type1>(t1), std::forward<Type2>(t2));
 }
 
@@ -174,10 +174,9 @@
 class MergeTuples {
  public:
   template <class Tuple>
-  auto operator()(Tuple&& value) const
-      -> decltype(add_to_tuple(
-          std::get<0>(std::forward<Tuple>(value)),
-          std::get<1>(std::forward<Tuple>(value)))) {
+  auto operator()(Tuple&& value) const -> decltype(add_to_tuple(
+      std::get<0>(std::forward<Tuple>(value)),
+      std::get<1>(std::forward<Tuple>(value)))) {
     static_assert(
         std::tuple_size<typename std::remove_reference<Tuple>::type>::value ==
             2,
diff --git a/folly/folly/gen/ParallelMap-inl.h b/folly/folly/gen/ParallelMap-inl.h
--- a/folly/folly/gen/ParallelMap-inl.h
+++ b/folly/folly/gen/ParallelMap-inl.h
@@ -29,6 +29,7 @@
 #include <folly/MPMCPipeline.h>
 #include <folly/functional/Invoke.h>
 #include <folly/synchronization/EventCount.h>
+#include <folly/system/HardwareConcurrency.h>
 
 namespace folly::gen::detail {
 
@@ -177,7 +178,7 @@
     Generator(Source source, const Predicate& pred, size_t nThreads)
         : source_(std::move(source)),
           pred_(pred),
-          nThreads_(nThreads ? nThreads : sysconf(_SC_NPROCESSORS_ONLN)) {}
+          nThreads_(nThreads ? nThreads : folly::available_concurrency()) {}
 
     template <class Body>
     void foreach(Body&& body) const {
diff --git a/folly/folly/hash/Checksum.cpp b/folly/folly/hash/Checksum.cpp
--- a/folly/folly/hash/Checksum.cpp
+++ b/folly/folly/hash/Checksum.cpp
@@ -17,7 +17,7 @@
 #include <folly/hash/Checksum.h>
 
 #include <algorithm>
-#include <stdexcept>
+#include <stdexcept> // IWYU pragma: keep
 
 #include <boost/crc.hpp>
 
@@ -30,9 +30,8 @@
 #include <folly/external/fast-crc32/sse_crc32c_v8s3x3.h> // @manual
 #include <folly/hash/detail/ChecksumDetail.h>
 
-#if FOLLY_SSE_PREREQ(4, 2)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 #include <emmintrin.h>
-#include <nmmintrin.h>
 #endif
 
 namespace folly {
@@ -41,7 +40,7 @@
 
 uint32_t crc32c_sw(
     const uint8_t* data, size_t nbytes, uint32_t startingChecksum);
-#if FOLLY_SSE_PREREQ(4, 2)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 
 uint32_t crc32_sw(
     const uint8_t* data, size_t nbytes, uint32_t startingChecksum);
@@ -290,6 +289,29 @@
   } else {
     return detail::crc32c_combine_sw(crc1, crc2, crc2len - len);
   }
+}
+
+uint32_t crc32c_combine_seed(
+    uint32_t crc1, uint32_t crc2, size_t crc2len, uint32_t startingChecksum) {
+  if (startingChecksum == 0U) {
+    return crc32c_combine(crc1, crc2, crc2len);
+  }
+
+  crc1 ^= startingChecksum;
+  crc2 ^= startingChecksum;
+
+  // Append up to 32 bits of zeroes in the normal way
+  uint8_t data[4] = {0, 0, 0, 0};
+  auto len = crc2len & 3;
+  if (len) {
+    crc1 = crc32c(data, len, crc1);
+  }
+
+  auto result = detail::crc32c_hw_supported()
+      ? detail::crc32c_combine_hw(crc1, crc2, crc2len - len)
+      : detail::crc32c_combine_sw(crc1, crc2, crc2len - len);
+  result ^= startingChecksum;
+  return result;
 }
 
 } // namespace folly
diff --git a/folly/folly/hash/Checksum.h b/folly/folly/hash/Checksum.h
--- a/folly/folly/hash/Checksum.h
+++ b/folly/folly/hash/Checksum.h
@@ -91,4 +91,21 @@
    polynomial */
 uint32_t crc32c_combine(uint32_t crc1, uint32_t crc2, size_t crc2len);
 
+/**
+ * crc32c_combine_seed is the same as crc32c_combine. Unlike crc32c_combine that
+ * only works for crc32c computed using a starting checksum 0U, this method
+ * works for any starting checksum that is an uint32_t.
+ *
+ * @param crc1 First CRC checksum
+ * @param crc2 Second CRC checksum
+ * @param crc2len Length of the second buffer (in bytes)
+ * @param startingChecksum The initial checksum value used when computing BOTH
+ *                         crc1 and crc2. Both checksums must have been computed
+ *                         with the same starting value. Use ~0U for the
+ *                         standard CRC32C default.
+ * @return Combined CRC32C checksum
+ */
+uint32_t crc32c_combine_seed(
+    uint32_t crc1, uint32_t crc2, size_t crc2len, uint32_t startingChecksum);
+
 } // namespace folly
diff --git a/folly/folly/hash/FnvHash.h b/folly/folly/hash/FnvHash.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/hash/FnvHash.h
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+namespace folly {
+namespace hash {
+
+//  fnv
+//
+//  Fowler / Noll / Vo (FNV) Hash
+//    http://www.isthe.com/chongo/tech/comp/fnv/
+//
+//  Discouraged for poor performance in the smhasher suite.
+
+constexpr uint32_t fnv32_hash_start = 2166136261UL;
+constexpr uint32_t fnva32_hash_start = 2166136261UL;
+constexpr uint64_t fnv64_hash_start = 14695981039346656037ULL;
+constexpr uint64_t fnva64_hash_start = 14695981039346656037ULL;
+
+/**
+ * Append byte to FNV hash.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+constexpr uint32_t fnv32_append_byte_BROKEN(uint32_t hash, uint8_t c) noexcept {
+  hash = hash //
+      + (hash << 1) //
+      + (hash << 4) //
+      + (hash << 7) //
+      + (hash << 8) //
+      + (hash << 24);
+  // forcing signed char, since other platforms can use unsigned
+  hash ^= static_cast<int8_t>(c);
+  return hash;
+}
+
+namespace detail {
+
+template <typename T>
+constexpr bool is_hashable_byte_v = false;
+template <>
+inline constexpr bool is_hashable_byte_v<char> = true;
+template <>
+inline constexpr bool is_hashable_byte_v<signed char> = true;
+template <>
+inline constexpr bool is_hashable_byte_v<unsigned char> = true;
+
+} // namespace detail
+
+/**
+ * FNV hash of a byte-range.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
+constexpr uint32_t fnv32_buf_BROKEN(
+    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
+  for (size_t i = 0; i < n; ++i) {
+    hash = fnv32_append_byte_BROKEN(hash, static_cast<uint8_t>(buf[i]));
+  }
+  return hash;
+}
+inline uint32_t fnv32_buf_BROKEN(
+    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
+  return fnv32_buf_BROKEN(reinterpret_cast<const uint8_t*>(buf), n, hash);
+}
+
+/**
+ * FNV hash of a c-str.
+ *
+ * Continues hashing until a null byte is reached.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @methodset fnv
+ */
+constexpr uint32_t fnv32_BROKEN(
+    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {
+  for (; *buf; ++buf) {
+    hash = fnv32_append_byte_BROKEN(hash, static_cast<uint8_t>(*buf));
+  }
+  return hash;
+}
+
+/**
+ * @overloadbrief FNV hash of a string.
+ *
+ * FNV is the Fowler / Noll / Vo Hash:
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * Discouraged for poor performance in the smhasher suite.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @methodset fnv
+ */
+inline uint32_t fnv32_BROKEN(
+    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {
+  return fnv32_buf_BROKEN(str.data(), str.size(), hash);
+}
+
+/**
+ * Append byte to FNV hash.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+constexpr uint32_t fnv32_append_byte_FIXED(uint32_t hash, uint8_t c) noexcept {
+  hash = hash //
+      + (hash << 1) //
+      + (hash << 4) //
+      + (hash << 7) //
+      + (hash << 8) //
+      + (hash << 24);
+  // forcing unsigned char
+  hash ^= static_cast<uint8_t>(c);
+  return hash;
+}
+
+/**
+ * FNV hash of a byte-range.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
+constexpr uint32_t fnv32_buf_FIXED(
+    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
+  for (size_t i = 0; i < n; ++i) {
+    hash = fnv32_append_byte_FIXED(hash, static_cast<uint8_t>(buf[i]));
+  }
+  return hash;
+}
+inline uint32_t fnv32_buf_FIXED(
+    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
+  return fnv32_buf_FIXED(reinterpret_cast<const uint8_t*>(buf), n, hash);
+}
+
+/**
+ * FNV hash of a c-str.
+ *
+ * Continues hashing until a null byte is reached.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @methodset fnv
+ */
+constexpr uint32_t fnv32_FIXED(
+    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {
+  for (; *buf; ++buf) {
+    hash = fnv32_append_byte_FIXED(hash, static_cast<uint8_t>(*buf));
+  }
+  return hash;
+}
+
+/**
+ * @overloadbrief FNV hash of a string.
+ *
+ * FNV is the Fowler / Noll / Vo Hash:
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * Discouraged for poor performance in the smhasher suite.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @methodset fnv
+ */
+inline uint32_t fnv32_FIXED(
+    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {
+  return fnv32_buf_FIXED(str.data(), str.size(), hash);
+}
+
+/**
+ * Append a byte to FNVA hash.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+constexpr uint32_t fnva32_append_byte(uint32_t hash, uint8_t c) noexcept {
+  hash ^= c;
+  hash = hash //
+      + (hash << 1) //
+      + (hash << 4) //
+      + (hash << 7) //
+      + (hash << 8) //
+      + (hash << 24);
+  return hash;
+}
+
+/**
+ * FNVA hash of a byte-range.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
+constexpr uint32_t fnva32_buf(
+    const C* buf, size_t n, uint32_t hash = fnva32_hash_start) noexcept {
+  for (size_t i = 0; i < n; ++i) {
+    hash = fnva32_append_byte(hash, static_cast<uint8_t>(buf[i]));
+  }
+  return hash;
+}
+inline uint32_t fnva32_buf(
+    const void* buf, size_t n, uint32_t hash = fnva32_hash_start) noexcept {
+  return fnva32_buf(reinterpret_cast<const uint8_t*>(buf), n, hash);
+}
+
+/**
+ * FNVA hash of a string.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+inline uint32_t fnva32(
+    const std::string& str, uint32_t hash = fnva32_hash_start) noexcept {
+  return fnva32_buf(str.data(), str.size(), hash);
+}
+
+/**
+ * Append a byte to FNV hash.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+constexpr uint64_t fnv64_append_byte_FIXED(uint64_t hash, uint8_t c) {
+  hash = hash //
+      + (hash << 1) //
+      + (hash << 4) //
+      + (hash << 5) //
+      + (hash << 7) //
+      + (hash << 8) //
+      + (hash << 40);
+  // forcing unsigned char
+  hash ^= static_cast<uint8_t>(c);
+  return hash;
+}
+
+/**
+ * FNV hash of a byte-range.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
+constexpr uint64_t fnv64_buf_FIXED(
+    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
+  for (size_t i = 0; i < n; ++i) {
+    hash = fnv64_append_byte_FIXED(hash, static_cast<uint8_t>(buf[i]));
+  }
+  return hash;
+}
+inline uint64_t fnv64_buf_FIXED(
+    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
+  return fnv64_buf_FIXED(reinterpret_cast<const uint8_t*>(buf), n, hash);
+}
+
+/**
+ * FNV hash of a c-str.
+ *
+ * Continues hashing until a null byte is reached.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+constexpr uint64_t fnv64_FIXED(
+    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {
+  for (; *buf; ++buf) {
+    hash = fnv64_append_byte_FIXED(hash, static_cast<uint8_t>(*buf));
+  }
+  return hash;
+}
+
+/**
+ * @overloadbrief FNV hash of a string.
+ *
+ * FNV is the Fowler / Noll / Vo Hash:
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * Discouraged for poor performance in the smhasher suite.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+inline uint64_t fnv64_FIXED(
+    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {
+  return fnv64_buf_FIXED(str.data(), str.size(), hash);
+}
+
+/**
+ * Append a byte to FNV hash.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+constexpr uint64_t fnv64_append_byte_BROKEN(uint64_t hash, uint8_t c) {
+  hash = hash //
+      + (hash << 1) //
+      + (hash << 4) //
+      + (hash << 5) //
+      + (hash << 7) //
+      + (hash << 8) //
+      + (hash << 40);
+  // forcing signed char, since other platforms can use unsigned
+  hash ^= static_cast<int8_t>(c);
+  return hash;
+}
+
+/**
+ * FNV hash of a byte-range.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
+constexpr uint64_t fnv64_buf_BROKEN(
+    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
+  for (size_t i = 0; i < n; ++i) {
+    hash = fnv64_append_byte_BROKEN(hash, static_cast<uint8_t>(buf[i]));
+  }
+  return hash;
+}
+inline uint64_t fnv64_buf_BROKEN(
+    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
+  return fnv64_buf_BROKEN(reinterpret_cast<const uint8_t*>(buf), n, hash);
+}
+
+/**
+ * FNV hash of a c-str.
+ *
+ * Continues hashing until a null byte is reached.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+constexpr uint64_t fnv64_BROKEN(
+    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {
+  for (; *buf; ++buf) {
+    hash = fnv64_append_byte_BROKEN(hash, static_cast<uint8_t>(*buf));
+  }
+  return hash;
+}
+
+/**
+ * @overloadbrief FNV hash of a string.
+ *
+ * FNV is the Fowler / Noll / Vo Hash:
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * Discouraged for poor performance in the smhasher suite.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+inline uint64_t fnv64_BROKEN(
+    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {
+  return fnv64_buf_BROKEN(str.data(), str.size(), hash);
+}
+
+/**
+ * Alias for fnv32_append_byte_BROKEN.
+ *
+ * @see fnv32_BROKEN
+ * @methodset fnv
+ */
+constexpr uint32_t fnv32_append_byte(uint32_t hash, uint8_t c) noexcept {
+  return fnv32_append_byte_BROKEN(hash, c);
+}
+
+/**
+ * Alias for fnv32_buf_BROKEN.
+ *
+ * @see fnv32_BROKEN
+ * @methodset fnv
+ */
+template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
+constexpr uint32_t fnv32_buf(
+    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
+  return fnv32_buf_BROKEN(buf, n, hash);
+}
+inline uint32_t fnv32_buf(
+    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
+  return fnv32_buf_BROKEN(buf, n, hash);
+}
+
+/**
+ * Alias for fnv32_BROKEN.
+ *
+ * @methodset fnv
+ */
+constexpr uint32_t fnv32(
+    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {
+  return fnv32_BROKEN(buf, hash);
+}
+
+/**
+ * Alias for fnv32_BROKEN.
+ *
+ * @methodset fnv
+ */
+inline uint32_t fnv32(
+    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {
+  return fnv32_BROKEN(str, hash);
+}
+
+/**
+ * Alias for fnv64_append_byte_BROKEN.
+ *
+ * @see fnv32_BROKEN
+ * @methodset fnv
+ */
+constexpr uint64_t fnv64_append_byte(uint64_t hash, uint8_t c) {
+  return fnv64_append_byte_BROKEN(hash, c);
+}
+
+/**
+ * Alias for fnv64_buf_BROKEN.
+ *
+ * @see fnv32_BROKEN
+ * @methodset fnv
+ */
+template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
+constexpr uint64_t fnv64_buf(
+    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
+  return fnv64_buf_BROKEN(buf, n, hash);
+}
+inline uint64_t fnv64_buf(
+    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
+  return fnv64_buf_BROKEN(buf, n, hash);
+}
+
+/**
+ * Alias for fnv64_BROKEN.
+ *
+ * @see fnv32_BROKEN
+ * @methodset fnv
+ */
+constexpr uint64_t fnv64(
+    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {
+  return fnv64_BROKEN(buf, hash);
+}
+
+/**
+ * Alias for fnv64_BROKEN.
+ *
+ * @see fnv32_BROKEN
+ * @methodset fnv
+ */
+inline uint64_t fnv64(
+    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {
+  return fnv64_BROKEN(str, hash);
+}
+
+/**
+ * Append a byte to FNVA hash.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+constexpr uint64_t fnva64_append_byte(uint64_t hash, uint8_t c) {
+  hash ^= c;
+  hash = hash //
+      + (hash << 1) //
+      + (hash << 4) //
+      + (hash << 5) //
+      + (hash << 7) //
+      + (hash << 8) //
+      + (hash << 40);
+  return hash;
+}
+
+/**
+ * FNVA hash of a byte-range.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
+constexpr uint64_t fnva64_buf(
+    const C* buf, size_t n, uint64_t hash = fnva64_hash_start) noexcept {
+  for (size_t i = 0; i < n; ++i) {
+    hash = fnva64_append_byte(hash, static_cast<uint8_t>(buf[i]));
+  }
+  return hash;
+}
+inline uint64_t fnva64_buf(
+    const void* buf, size_t n, uint64_t hash = fnva64_hash_start) noexcept {
+  return fnva64_buf(reinterpret_cast<const uint8_t*>(buf), n, hash);
+}
+
+/**
+ * FNVA hash of a string.
+ *
+ * @param hash  The initial hash seed.
+ *
+ * @see fnv32
+ * @methodset fnv
+ */
+inline uint64_t fnva64(
+    const std::string& str, uint64_t hash = fnva64_hash_start) noexcept {
+  return fnva64_buf(str.data(), str.size(), hash);
+}
+
+} // namespace hash
+} // namespace folly
diff --git a/folly/folly/hash/Hash.h b/folly/folly/hash/Hash.h
--- a/folly/folly/hash/Hash.h
+++ b/folly/folly/hash/Hash.h
@@ -42,32 +42,20 @@
 #include <folly/Portability.h>
 #include <folly/Traits.h>
 #include <folly/Utility.h>
-#include <folly/functional/ApplyTuple.h>
 #include <folly/hash/MurmurHash.h>
 #include <folly/hash/SpookyHashV1.h>
 #include <folly/hash/SpookyHashV2.h>
 #include <folly/lang/Bits.h>
 
+// This includes are here for backward compatibility as these algorithms were
+// kept in this header.
+#include <folly/hash/FnvHash.h>
+#include <folly/hash/HsiehHash.h>
+#include <folly/lang/cstring_view.h>
+
 namespace folly {
 namespace hash {
 
-namespace detail {
-
-namespace {
-
-template <typename T>
-constexpr bool is_hashable_byte_v = false;
-template <>
-constexpr bool is_hashable_byte_v<char> = true;
-template <>
-constexpr bool is_hashable_byte_v<signed char> = true;
-template <>
-constexpr bool is_hashable_byte_v<unsigned char> = true;
-
-} // namespace
-
-} // namespace detail
-
 /**
  * Reduce two 64-bit hashes into one.
  *
@@ -87,6 +75,38 @@
   return b;
 }
 
+namespace detail {
+
+template <typename H>
+constexpr uint64_t hash_sequence(const H&) noexcept {
+  return 0;
+}
+
+template <typename H, typename T>
+constexpr uint64_t hash_sequence(const H& h, const T& v) noexcept(
+    noexcept(h(v))) {
+  return h(v);
+}
+
+template <typename H, typename T, typename... Ts>
+constexpr uint64_t hash_sequence(
+    const H& h, const T& v, const Ts&... ts) noexcept(noexcept(h(v))) {
+  return hash::hash_128_to_64(h(v), hash_sequence(h, ts...));
+}
+
+template <typename H, typename Tuple, size_t... Is>
+uint64_t hash_tuple_sequence(
+    const H& h, const Tuple& t, std::index_sequence<Is...>) {
+  return hash_sequence(h, std::get<Is>(t)...);
+}
+
+template <typename H, typename... Ts>
+constexpr uint64_t hash_tuple(const H& h, const std::tuple<Ts...>& t) {
+  return hash_tuple_sequence(h, t, std::make_index_sequence<sizeof...(Ts)>());
+}
+
+} // namespace detail
+
 /**
  * Order-independent reduction of two 64-bit hashes into one.
  *
@@ -195,618 +215,6 @@
   return key;
 }
 
-//  fnv
-//
-//  Fowler / Noll / Vo (FNV) Hash
-//    http://www.isthe.com/chongo/tech/comp/fnv/
-//
-//  Discouraged for poor performance in the smhasher suite.
-
-constexpr uint32_t fnv32_hash_start = 2166136261UL;
-constexpr uint32_t fnva32_hash_start = 2166136261UL;
-constexpr uint64_t fnv64_hash_start = 14695981039346656037ULL;
-constexpr uint64_t fnva64_hash_start = 14695981039346656037ULL;
-
-/**
- * Append byte to FNV hash.
- *
- * @see fnv32
- * @methodset fnv
- */
-constexpr uint32_t fnv32_append_byte_BROKEN(uint32_t hash, uint8_t c) noexcept {
-  hash = hash //
-      + (hash << 1) //
-      + (hash << 4) //
-      + (hash << 7) //
-      + (hash << 8) //
-      + (hash << 24);
-  // forcing signed char, since other platforms can use unsigned
-  hash ^= static_cast<int8_t>(c);
-  return hash;
-}
-
-/**
- * FNV hash of a byte-range.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
-constexpr uint32_t fnv32_buf_BROKEN(
-    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
-  for (size_t i = 0; i < n; ++i) {
-    hash = fnv32_append_byte_BROKEN(hash, static_cast<uint8_t>(buf[i]));
-  }
-  return hash;
-}
-inline uint32_t fnv32_buf_BROKEN(
-    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
-  return fnv32_buf_BROKEN(reinterpret_cast<const uint8_t*>(buf), n, hash);
-}
-
-/**
- * FNV hash of a c-str.
- *
- * Continues hashing until a null byte is reached.
- *
- * @param hash  The initial hash seed.
- *
- * @methodset fnv
- */
-constexpr uint32_t fnv32_BROKEN(
-    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {
-  for (; *buf; ++buf) {
-    hash = fnv32_append_byte_BROKEN(hash, static_cast<uint8_t>(*buf));
-  }
-  return hash;
-}
-
-/**
- * @overloadbrief FNV hash of a string.
- *
- * FNV is the Fowler / Noll / Vo Hash:
- * http://www.isthe.com/chongo/tech/comp/fnv/
- *
- * Discouraged for poor performance in the smhasher suite.
- *
- * @param hash  The initial hash seed.
- *
- * @methodset fnv
- */
-inline uint32_t fnv32_BROKEN(
-    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {
-  return fnv32_buf_BROKEN(str.data(), str.size(), hash);
-}
-
-/**
- * Append byte to FNV hash.
- *
- * @see fnv32
- * @methodset fnv
- */
-constexpr uint32_t fnv32_append_byte_FIXED(uint32_t hash, uint8_t c) noexcept {
-  hash = hash //
-      + (hash << 1) //
-      + (hash << 4) //
-      + (hash << 7) //
-      + (hash << 8) //
-      + (hash << 24);
-  // forcing unsigned char
-  hash ^= static_cast<uint8_t>(c);
-  return hash;
-}
-
-/**
- * FNV hash of a byte-range.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
-constexpr uint32_t fnv32_buf_FIXED(
-    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
-  for (size_t i = 0; i < n; ++i) {
-    hash = fnv32_append_byte_FIXED(hash, static_cast<uint8_t>(buf[i]));
-  }
-  return hash;
-}
-inline uint32_t fnv32_buf_FIXED(
-    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
-  return fnv32_buf_FIXED(reinterpret_cast<const uint8_t*>(buf), n, hash);
-}
-
-/**
- * FNV hash of a c-str.
- *
- * Continues hashing until a null byte is reached.
- *
- * @param hash  The initial hash seed.
- *
- * @methodset fnv
- */
-constexpr uint32_t fnv32_FIXED(
-    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {
-  for (; *buf; ++buf) {
-    hash = fnv32_append_byte_FIXED(hash, static_cast<uint8_t>(*buf));
-  }
-  return hash;
-}
-
-/**
- * @overloadbrief FNV hash of a string.
- *
- * FNV is the Fowler / Noll / Vo Hash:
- * http://www.isthe.com/chongo/tech/comp/fnv/
- *
- * Discouraged for poor performance in the smhasher suite.
- *
- * @param hash  The initial hash seed.
- *
- * @methodset fnv
- */
-inline uint32_t fnv32_FIXED(
-    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {
-  return fnv32_buf_FIXED(str.data(), str.size(), hash);
-}
-
-/**
- * Append a byte to FNVA hash.
- *
- * @see fnv32
- * @methodset fnv
- */
-constexpr uint32_t fnva32_append_byte(uint32_t hash, uint8_t c) noexcept {
-  hash ^= c;
-  hash = hash //
-      + (hash << 1) //
-      + (hash << 4) //
-      + (hash << 7) //
-      + (hash << 8) //
-      + (hash << 24);
-  return hash;
-}
-
-/**
- * FNVA hash of a byte-range.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
-constexpr uint32_t fnva32_buf(
-    const C* buf, size_t n, uint32_t hash = fnva32_hash_start) noexcept {
-  for (size_t i = 0; i < n; ++i) {
-    hash = fnva32_append_byte(hash, static_cast<uint8_t>(buf[i]));
-  }
-  return hash;
-}
-inline uint32_t fnva32_buf(
-    const void* buf, size_t n, uint32_t hash = fnva32_hash_start) noexcept {
-  return fnva32_buf(reinterpret_cast<const uint8_t*>(buf), n, hash);
-}
-
-/**
- * FNVA hash of a string.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-inline uint32_t fnva32(
-    const std::string& str, uint32_t hash = fnva32_hash_start) noexcept {
-  return fnva32_buf(str.data(), str.size(), hash);
-}
-
-/**
- * Append a byte to FNV hash.
- *
- * @see fnv32
- * @methodset fnv
- */
-constexpr uint64_t fnv64_append_byte_FIXED(uint64_t hash, uint8_t c) {
-  hash = hash //
-      + (hash << 1) //
-      + (hash << 4) //
-      + (hash << 5) //
-      + (hash << 7) //
-      + (hash << 8) //
-      + (hash << 40);
-  // forcing unsigned char
-  hash ^= static_cast<uint8_t>(c);
-  return hash;
-}
-
-/**
- * FNV hash of a byte-range.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
-constexpr uint64_t fnv64_buf_FIXED(
-    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
-  for (size_t i = 0; i < n; ++i) {
-    hash = fnv64_append_byte_FIXED(hash, static_cast<uint8_t>(buf[i]));
-  }
-  return hash;
-}
-inline uint64_t fnv64_buf_FIXED(
-    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
-  return fnv64_buf_FIXED(reinterpret_cast<const uint8_t*>(buf), n, hash);
-}
-
-/**
- * FNV hash of a c-str.
- *
- * Continues hashing until a null byte is reached.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-constexpr uint64_t fnv64_FIXED(
-    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {
-  for (; *buf; ++buf) {
-    hash = fnv64_append_byte_FIXED(hash, static_cast<uint8_t>(*buf));
-  }
-  return hash;
-}
-
-/**
- * @overloadbrief FNV hash of a string.
- *
- * FNV is the Fowler / Noll / Vo Hash:
- * http://www.isthe.com/chongo/tech/comp/fnv/
- *
- * Discouraged for poor performance in the smhasher suite.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-inline uint64_t fnv64_FIXED(
-    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {
-  return fnv64_buf_FIXED(str.data(), str.size(), hash);
-}
-
-/**
- * Append a byte to FNV hash.
- *
- * @see fnv32
- * @methodset fnv
- */
-constexpr uint64_t fnv64_append_byte_BROKEN(uint64_t hash, uint8_t c) {
-  hash = hash //
-      + (hash << 1) //
-      + (hash << 4) //
-      + (hash << 5) //
-      + (hash << 7) //
-      + (hash << 8) //
-      + (hash << 40);
-  // forcing signed char, since other platforms can use unsigned
-  hash ^= static_cast<int8_t>(c);
-  return hash;
-}
-
-/**
- * FNV hash of a byte-range.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
-constexpr uint64_t fnv64_buf_BROKEN(
-    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
-  for (size_t i = 0; i < n; ++i) {
-    hash = fnv64_append_byte_BROKEN(hash, static_cast<uint8_t>(buf[i]));
-  }
-  return hash;
-}
-inline uint64_t fnv64_buf_BROKEN(
-    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
-  return fnv64_buf_BROKEN(reinterpret_cast<const uint8_t*>(buf), n, hash);
-}
-
-/**
- * FNV hash of a c-str.
- *
- * Continues hashing until a null byte is reached.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-constexpr uint64_t fnv64_BROKEN(
-    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {
-  for (; *buf; ++buf) {
-    hash = fnv64_append_byte_BROKEN(hash, static_cast<uint8_t>(*buf));
-  }
-  return hash;
-}
-
-/**
- * @overloadbrief FNV hash of a string.
- *
- * FNV is the Fowler / Noll / Vo Hash:
- * http://www.isthe.com/chongo/tech/comp/fnv/
- *
- * Discouraged for poor performance in the smhasher suite.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-inline uint64_t fnv64_BROKEN(
-    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {
-  return fnv64_buf_BROKEN(str.data(), str.size(), hash);
-}
-
-/**
- * Alias for fnv32_append_byte_BROKEN.
- *
- * @see fnv32_BROKEN
- * @methodset fnv
- */
-constexpr uint32_t fnv32_append_byte(uint32_t hash, uint8_t c) noexcept {
-  return fnv32_append_byte_BROKEN(hash, c);
-}
-
-/**
- * Alias for fnv32_buf_BROKEN.
- *
- * @see fnv32_BROKEN
- * @methodset fnv
- */
-template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
-constexpr uint32_t fnv32_buf(
-    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
-  return fnv32_buf_BROKEN(buf, n, hash);
-}
-inline uint32_t fnv32_buf(
-    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {
-  return fnv32_buf_BROKEN(buf, n, hash);
-}
-
-/**
- * Alias for fnv32_BROKEN.
- *
- * @methodset fnv
- */
-constexpr uint32_t fnv32(
-    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {
-  return fnv32_BROKEN(buf, hash);
-}
-
-/**
- * Alias for fnv32_BROKEN.
- *
- * @methodset fnv
- */
-inline uint32_t fnv32(
-    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {
-  return fnv32_BROKEN(str, hash);
-}
-
-/**
- * Alias for fnv64_append_byte_BROKEN.
- *
- * @see fnv32_BROKEN
- * @methodset fnv
- */
-constexpr uint64_t fnv64_append_byte(uint64_t hash, uint8_t c) {
-  return fnv64_append_byte_BROKEN(hash, c);
-}
-
-/**
- * Alias for fnv64_buf_BROKEN.
- *
- * @see fnv32_BROKEN
- * @methodset fnv
- */
-template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
-constexpr uint64_t fnv64_buf(
-    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
-  return fnv64_buf_BROKEN(buf, n, hash);
-}
-inline uint64_t fnv64_buf(
-    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {
-  return fnv64_buf_BROKEN(buf, n, hash);
-}
-
-/**
- * Alias for fnv64_BROKEN.
- *
- * @see fnv32_BROKEN
- * @methodset fnv
- */
-constexpr uint64_t fnv64(
-    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {
-  return fnv64_BROKEN(buf, hash);
-}
-
-/**
- * Alias for fnv64_BROKEN.
- *
- * @see fnv32_BROKEN
- * @methodset fnv
- */
-inline uint64_t fnv64(
-    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {
-  return fnv64_BROKEN(str, hash);
-}
-
-/**
- * Append a byte to FNVA hash.
- *
- * @see fnv32
- * @methodset fnv
- */
-constexpr uint64_t fnva64_append_byte(uint64_t hash, uint8_t c) {
-  hash ^= c;
-  hash = hash //
-      + (hash << 1) //
-      + (hash << 4) //
-      + (hash << 5) //
-      + (hash << 7) //
-      + (hash << 8) //
-      + (hash << 40);
-  return hash;
-}
-
-/**
- * FNVA hash of a byte-range.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-template <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>
-constexpr uint64_t fnva64_buf(
-    const C* buf, size_t n, uint64_t hash = fnva64_hash_start) noexcept {
-  for (size_t i = 0; i < n; ++i) {
-    hash = fnva64_append_byte(hash, static_cast<uint8_t>(buf[i]));
-  }
-  return hash;
-}
-inline uint64_t fnva64_buf(
-    const void* buf, size_t n, uint64_t hash = fnva64_hash_start) noexcept {
-  return fnva64_buf(reinterpret_cast<const uint8_t*>(buf), n, hash);
-}
-
-/**
- * FNVA hash of a string.
- *
- * @param hash  The initial hash seed.
- *
- * @see fnv32
- * @methodset fnv
- */
-inline uint64_t fnva64(
-    const std::string& str, uint64_t hash = fnva64_hash_start) noexcept {
-  return fnva64_buf(str.data(), str.size(), hash);
-}
-
-//  hsieh
-//
-//  Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html
-
-#define get16bits(d) folly::loadUnaligned<uint16_t>(d)
-
-/**
- * hsieh hash a byte-range.
- *
- * @see hsieh_hash32_str
- * @methodset hsieh
- */
-inline constexpr uint32_t hsieh_hash32_buf_constexpr(
-    const unsigned char* buf, size_t len) noexcept {
-  // forcing signed char, since other platforms can use unsigned
-  const unsigned char* s = buf;
-  uint32_t hash = static_cast<uint32_t>(len);
-  uint32_t tmp = 0;
-  size_t rem = 0;
-
-  if (len <= 0 || buf == nullptr) {
-    return 0;
-  }
-
-  rem = len & 3;
-  len >>= 2;
-
-  /* Main loop */
-  for (; len > 0; len--) {
-    hash += get16bits(s);
-    tmp = (get16bits(s + 2) << 11) ^ hash;
-    hash = (hash << 16) ^ tmp;
-    s += 2 * sizeof(uint16_t);
-    hash += hash >> 11;
-  }
-
-  /* Handle end cases */
-  switch (rem) {
-    case 3:
-      hash += get16bits(s);
-      hash ^= hash << 16;
-      hash ^= s[sizeof(uint16_t)] << 18;
-      hash += hash >> 11;
-      break;
-    case 2:
-      hash += get16bits(s);
-      hash ^= hash << 11;
-      hash += hash >> 17;
-      break;
-    case 1:
-      hash += *s;
-      hash ^= hash << 10;
-      hash += hash >> 1;
-      break;
-    default:
-      break;
-  }
-
-  /* Force "avalanching" of final 127 bits */
-  hash ^= hash << 3;
-  hash += hash >> 5;
-  hash ^= hash << 4;
-  hash += hash >> 17;
-  hash ^= hash << 25;
-  hash += hash >> 6;
-
-  return hash;
-}
-
-#undef get16bits
-
-/**
- * hsieh hash a void* byte-range.
- *
- * @see hsieh_hash32_str
- * @methodset hsieh
- */
-inline uint32_t hsieh_hash32_buf(const void* buf, size_t len) noexcept {
-  return hsieh_hash32_buf_constexpr(
-      reinterpret_cast<const unsigned char*>(buf), len);
-}
-
-/**
- * hsieh hash a c-str.
- *
- * Computes the strlen of the input, then byte-range hashes it.
- *
- * @see hsieh_hash32_str
- * @methodset hsieh
- */
-inline uint32_t hsieh_hash32(const char* s) noexcept {
-  return hsieh_hash32_buf(s, std::strlen(s));
-}
-
-/**
- * hsieh hash a string.
- *
- * Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html
- *
- * @methodset hsieh
- */
-inline uint32_t hsieh_hash32_str(const std::string& str) noexcept {
-  return hsieh_hash32_buf(str.data(), str.size());
-}
-
 } // namespace hash
 
 namespace detail {
@@ -863,12 +271,14 @@
     return hasher<T>()(v);
   }
 
-  template <class T, class... Ts>
-  constexpr size_t operator()(const T& t, const Ts&... ts) const {
-    return hash::hash_128_to_64((*this)(t), (*this)(ts...));
+  template <class... Ts>
+  constexpr size_t operator()(const Ts&... ts) const {
+    return static_cast<size_t>(hash::detail::hash_sequence(*this, ts...));
   }
 
-  constexpr size_t operator()() const noexcept { return 0; }
+  constexpr size_t operator()() const noexcept {
+    return static_cast<size_t>(hash::detail::hash_sequence(*this));
+  }
 };
 
 // IsAvalanchingHasher<H, K> extends std::integral_constant<bool, V>.
@@ -1018,9 +428,17 @@
 template <typename K>
 struct IsAvalanchingHasher<hasher<std::string_view>, K> : std::true_type {};
 
+template <>
+struct hasher<cstring_view> : hasher<std::string_view> {};
+template <typename K>
+struct IsAvalanchingHasher<hasher<cstring_view>, K> : std::true_type {};
+
 template <typename T>
 struct hasher<T, std::enable_if_t<std::is_enum<T>::value>> {
-  size_t operator()(T key) const noexcept { return Hash()(to_underlying(key)); }
+  size_t operator()(T key) const noexcept {
+    auto u = to_underlying(key);
+    return hasher<decltype(u)>{}(u);
+  }
 };
 
 template <typename T, typename K>
@@ -1028,19 +446,31 @@
     hasher<T, std::enable_if_t<std::is_enum<T>::value>>,
     K> : IsAvalanchingHasher<hasher<std::underlying_type_t<T>>, K> {};
 
+namespace detail {
+struct hash_one_fn {
+  template <typename T>
+  constexpr size_t operator()(T const& v) const
+      noexcept(noexcept(hasher<T>{}(v))) {
+    return hasher<T>{}(v);
+  }
+};
+
+inline constexpr hash_one_fn hash_one{};
+} // namespace detail
+
 template <typename T1, typename T2>
 struct hasher<std::pair<T1, T2>> {
   using folly_is_avalanching = std::true_type;
 
   size_t operator()(const std::pair<T1, T2>& key) const {
-    return Hash()(key.first, key.second);
+    return hash::detail::hash_sequence(detail::hash_one, key.first, key.second);
   }
 };
 
 template <typename... Ts>
 struct hasher<std::tuple<Ts...>> {
   size_t operator()(const std::tuple<Ts...>& key) const {
-    return apply(Hash(), key);
+    return hash::detail::hash_tuple(detail::hash_one, key);
   }
 };
 
@@ -1049,7 +479,8 @@
   using folly_is_avalanching = hasher<std::uintptr_t>::folly_is_avalanching;
 
   size_t operator()(T* key) const {
-    return Hash()(folly::bit_cast<std::uintptr_t>(key));
+    auto val = folly::bit_cast<std::uintptr_t>(key);
+    return hasher<decltype(val)>{}(val);
   }
 };
 
@@ -1058,7 +489,7 @@
   using folly_is_avalanching = typename hasher<T*>::folly_is_avalanching;
 
   size_t operator()(const std::unique_ptr<T>& key) const {
-    return Hash()(key.get());
+    return hasher<T*>{}(key.get());
   }
 };
 
@@ -1067,7 +498,7 @@
   using folly_is_avalanching = typename hasher<T*>::folly_is_avalanching;
 
   size_t operator()(const std::shared_ptr<T>& key) const {
-    return Hash()(key.get());
+    return hasher<T*>{}(key.get());
   }
 };
 
@@ -1251,7 +682,7 @@
  * @methodset ranges
  */
 template <typename T, typename... Ts>
-FOLLY_NODISCARD size_t hash_combine(const T& t, const Ts&... ts) noexcept(
+[[nodiscard]] size_t hash_combine(const T& t, const Ts&... ts) noexcept(
     noexcept(hash_combine_generic(StdHasher{}, t, ts...))) {
   return hash_combine_generic(StdHasher{}, t, ts...);
 }
@@ -1329,10 +760,13 @@
 // std::hash for float and double on libstdc++-v3 are avalanching,
 // but they are not on libc++.  std::hash for integral types is not
 // avalanching for libstdc++-v3 or libc++.  We're conservative here and
-// just mark std::string as avalanching.  std::string_view will also be
-// so, once it exists.
+// just mark std::string and std::string_view as avalanching.
 template <typename... Args, typename K>
 struct IsAvalanchingHasher<std::hash<std::basic_string<Args...>>, K>
+    : std::true_type {};
+
+template <typename... Args, typename K>
+struct IsAvalanchingHasher<std::hash<std::basic_string_view<Args...>>, K>
     : std::true_type {};
 
 } // namespace folly
diff --git a/folly/folly/hash/HsiehHash.h b/folly/folly/hash/HsiehHash.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/hash/HsiehHash.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <string>
+
+#include <folly/lang/Bits.h>
+
+namespace folly {
+namespace hash {
+
+//  hsieh
+//
+//  Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html
+//
+//  Discouraged for suboptimal performance in the smhasher suite.
+
+#define get16bits(d) folly::loadUnaligned<uint16_t>(d)
+
+/**
+ * hsieh hash a byte-range.
+ *
+ * @see hsieh_hash32_str
+ * @methodset hsieh
+ */
+inline constexpr uint32_t hsieh_hash32_buf_constexpr(
+    const unsigned char* buf, size_t len) noexcept {
+  // forcing signed char, since other platforms can use unsigned
+  const unsigned char* s = buf;
+  uint32_t hash = static_cast<uint32_t>(len);
+  uint32_t tmp = 0;
+  size_t rem = 0;
+
+  if (len <= 0 || buf == nullptr) {
+    return 0;
+  }
+
+  rem = len & 3;
+  len >>= 2;
+
+  /* Main loop */
+  for (; len > 0; len--) {
+    hash += get16bits(s);
+    tmp = (get16bits(s + 2) << 11) ^ hash;
+    hash = (hash << 16) ^ tmp;
+    s += 2 * sizeof(uint16_t);
+    hash += hash >> 11;
+  }
+
+  /* Handle end cases */
+  switch (rem) {
+    case 3:
+      hash += get16bits(s);
+      hash ^= hash << 16;
+      hash ^= s[sizeof(uint16_t)] << 18;
+      hash += hash >> 11;
+      break;
+    case 2:
+      hash += get16bits(s);
+      hash ^= hash << 11;
+      hash += hash >> 17;
+      break;
+    case 1:
+      hash += *s;
+      hash ^= hash << 10;
+      hash += hash >> 1;
+      break;
+    default:
+      break;
+  }
+
+  /* Force "avalanching" of final 127 bits */
+  hash ^= hash << 3;
+  hash += hash >> 5;
+  hash ^= hash << 4;
+  hash += hash >> 17;
+  hash ^= hash << 25;
+  hash += hash >> 6;
+
+  return hash;
+}
+
+#undef get16bits
+
+/**
+ * hsieh hash a void* byte-range.
+ *
+ * @see hsieh_hash32_str
+ * @methodset hsieh
+ */
+inline uint32_t hsieh_hash32_buf(const void* buf, size_t len) noexcept {
+  return hsieh_hash32_buf_constexpr(
+      reinterpret_cast<const unsigned char*>(buf), len);
+}
+
+/**
+ * hsieh hash a c-str.
+ *
+ * Computes the strlen of the input, then byte-range hashes it.
+ *
+ * @see hsieh_hash32_str
+ * @methodset hsieh
+ */
+inline uint32_t hsieh_hash32(const char* s) noexcept {
+  return hsieh_hash32_buf(s, std::strlen(s));
+}
+
+/**
+ * hsieh hash a string.
+ *
+ * Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html
+ *
+ * @methodset hsieh
+ */
+inline uint32_t hsieh_hash32_str(const std::string& str) noexcept {
+  return hsieh_hash32_buf(str.data(), str.size());
+}
+
+} // namespace hash
+} // namespace folly
diff --git a/folly/folly/hash/SpookyHashV1.cpp b/folly/folly/hash/SpookyHashV1.cpp
--- a/folly/folly/hash/SpookyHashV1.cpp
+++ b/folly/folly/hash/SpookyHashV1.cpp
@@ -28,8 +28,6 @@
 
 #include <cstring>
 
-#include <folly/CppAttributes.h>
-
 #define ALLOW_UNALIGNED_READS 1
 
 namespace folly {
diff --git a/folly/folly/hash/SpookyHashV2.cpp b/folly/folly/hash/SpookyHashV2.cpp
--- a/folly/folly/hash/SpookyHashV2.cpp
+++ b/folly/folly/hash/SpookyHashV2.cpp
@@ -30,7 +30,6 @@
 
 #include <cstring>
 
-#include <folly/CppAttributes.h>
 #include <folly/Portability.h>
 
 namespace folly {
diff --git a/folly/folly/hash/UniqueHashKey.cpp b/folly/folly/hash/UniqueHashKey.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/hash/UniqueHashKey.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/hash/UniqueHashKey.h>
+
+#include <folly/portability/OpenSSL.h>
+#include <folly/portability/Unistd.h>
+
+#if defined(__linux__)
+#include <sys/auxv.h>
+#endif
+
+#if __has_include(<blake3.h>)
+#include <blake3.h>
+#endif
+
+#if __has_include(<xxh3.h>)
+#include <xxh3.h>
+#endif
+
+namespace folly {
+
+namespace detail {
+
+/// unique_hash_key_init_process_key_sha256
+///
+/// Cryptographically hashes auxval-random or pid, keyed by caller __func__. For
+/// each algorithm, for extra margin of safety, we want a different process-key.
+///
+/// Accepts fixed-sized 32-byte output only.
+///
+/// SHA256 is not keyed, but HMAC requires two rounds of hashing. So this uses
+/// the Secret Suffix MAC construction, which depends on the underlying hash
+/// function's collision resistance for its own collision resistance.
+void unique_hash_key_init_process_key_sha256(
+    char const* const context, span<uint8_t, SHA256_DIGEST_LENGTH> const out) {
+  auto const pid = getpid();
+  auto const pidkey = reinterpret_span_cast<uint8_t const>(span{&pid, 1});
+#if defined(__linux__)
+  constexpr size_t auxlen = 16;
+  auto const auxval = getauxval(AT_RANDOM);
+  // NOLINTNEXTLINE(performance-no-int-to-ptr)
+  auto const auxptr = reinterpret_cast<uint8_t const*>(auxval);
+  auto const auxkey = span<uint8_t const>{auxptr, auxlen};
+#endif
+  SHA256_CTX h{};
+  SHA256_Init(&h);
+  SHA256_Update(&h, context, std::strlen(context));
+  SHA256_Update(&h, pidkey.data(), pidkey.size()); // Secret Suffix MAC
+#if defined(__linux__)
+  SHA256_Update(&h, auxkey.data(), auxkey.size()); // Secret Suffix MAC
+#endif
+  SHA256_Final(out.data(), &h);
+}
+
+struct unique_hash_key_algo_strong_sha256_init_t { // NOLINT
+  uint8_t key[SHA256_DIGEST_LENGTH] = {};
+};
+
+static unique_hash_key_algo_strong_sha256_init_t
+unique_hash_key_algo_strong_sha256_init_impl() {
+  unique_hash_key_algo_strong_sha256_init_t res{};
+  unique_hash_key_init_process_key_sha256(__func__, res.key);
+  return res;
+}
+
+// init blake3 hasher with key unique to this process
+static unique_hash_key_algo_strong_sha256_init_t const&
+unique_hash_key_algo_strong_sha256_init() {
+  static auto const object = unique_hash_key_algo_strong_sha256_init_impl();
+  return object;
+}
+
+#if __has_include(<blake3.h>)
+
+/// unique_hash_key_init_process_key_blake3
+///
+/// Cryptographically hashes auxval-random or pid, keyed by caller __func__. For
+/// each algorithm, for extra margin of safety, we want a different process-key.
+///
+/// Accepts arbitrarily-sized output, which is required by the xxh3 algo.
+void unique_hash_key_init_process_key_blake3(
+    char const* const context, span<uint8_t> const out) {
+#if defined(__linux__)
+  constexpr size_t auxlen = 16;
+  auto const auxval = getauxval(AT_RANDOM);
+  // NOLINTNEXTLINE(performance-no-int-to-ptr)
+  auto const auxptr = reinterpret_cast<uint8_t const*>(auxval);
+  auto const key = span<uint8_t const>{auxptr, auxlen};
+#else
+  auto const pid = getpid();
+  auto const key = reinterpret_span_cast<uint8_t const>(span{&pid, 1});
+#endif
+  blake3_hasher h{};
+  blake3_hasher_init_derive_key(&h, context);
+  blake3_hasher_update(&h, key.data(), key.size());
+  blake3_hasher_finalize(&h, out.data(), out.size());
+}
+
+struct unique_hash_key_algo_strong_blake3_init_t { // NOLINT
+  uint8_t key[BLAKE3_KEY_LEN] = {};
+};
+
+static unique_hash_key_algo_strong_blake3_init_t
+unique_hash_key_algo_strong_blake3_init_impl() {
+  unique_hash_key_algo_strong_blake3_init_t res{};
+  unique_hash_key_init_process_key_blake3(__func__, res.key);
+  return res;
+}
+
+// init blake3 hasher with key unique to this process
+static unique_hash_key_algo_strong_blake3_init_t const&
+unique_hash_key_algo_strong_blake3_init() {
+  static auto const object = unique_hash_key_algo_strong_blake3_init_impl();
+  return object;
+}
+
+#if __has_include(<xxh3.h>)
+
+struct unique_hash_key_algo_fast_xxh3_init_t { // NOLINT
+  uint8_t secret[XXH3_SECRET_SIZE_MIN] = {};
+  uint64_t seed = {};
+};
+
+static unique_hash_key_algo_fast_xxh3_init_t
+unique_hash_key_algo_fast_xxh3_init_impl() {
+  unique_hash_key_algo_fast_xxh3_init_t res{};
+  unique_hash_key_init_process_key_blake3(__func__, res.secret);
+  res.seed = XXH3_64bits(res.secret, sizeof(res.secret));
+  return res;
+}
+
+// init xxh3 hasher with secret/seed unique to this process
+static unique_hash_key_algo_fast_xxh3_init_t const&
+unique_hash_key_algo_fast_xxh3_init() {
+  static auto const object = unique_hash_key_algo_fast_xxh3_init_impl();
+  return object;
+}
+
+template <size_t Size>
+struct unique_hash_key_algo_fast_xxh3_ops;
+template <>
+struct unique_hash_key_algo_fast_xxh3_ops<8> {
+  static constexpr auto init = XXH3_64bits_reset_withSecret;
+  static constexpr auto update = XXH3_64bits_update;
+  static constexpr auto finalize = XXH3_64bits_digest;
+};
+template <>
+struct unique_hash_key_algo_fast_xxh3_ops<16> {
+  static constexpr auto init = XXH3_128bits_reset_withSecret;
+  static constexpr auto update = XXH3_128bits_update;
+  static constexpr auto finalize = XXH3_128bits_digest;
+};
+
+#endif // __has_include(<xxh3.h>)
+
+#endif // __has_include(<blake3.h>)
+
+template <typename Hash, typename Update>
+FOLLY_ALWAYS_INLINE static void unique_hash_key_hash_items(
+    Update const update,
+    Hash* const h,
+    span<detail::unique_hash_key_item const> const in) noexcept {
+  for (auto const item : in) {
+    auto const [len, include_len] = item.unpack_len();
+    if (include_len) {
+      auto const buf = reinterpret_cast<uint8_t const*>(&len);
+      update(h, buf, sizeof(len));
+    }
+    update(h, item.buf, len);
+  }
+}
+
+} // namespace detail
+
+/// SHA256 is not keyed, but HMAC requires two rounds of hashing. So this uses
+/// the Secret Suffix MAC construction, which depends on the underlying hash
+/// function's collision resistance for its own collision resistance.
+template <size_t Size>
+std::array<uint8_t, Size>
+unique_hash_key_algo_strong_sha256_fn<Size>::operator()(
+    span<detail::unique_hash_key_item const> const in) const noexcept {
+  auto const& init = detail::unique_hash_key_algo_strong_sha256_init();
+  SHA256_CTX h;
+  SHA256_Init(&h);
+  detail::unique_hash_key_hash_items(SHA256_Update, &h, in);
+  SHA256_Update(&h, init.key, sizeof(init.key)); // Secret Suffix MAC
+  unsigned char mid[SHA256_DIGEST_LENGTH];
+  SHA256_Final(mid, &h);
+  std::array<uint8_t, Size> out{};
+  std::memcpy(out.data(), mid, out.size());
+  return out;
+}
+
+template std::array<uint8_t, 8> //
+unique_hash_key_algo_strong_sha256_fn<8>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+template std::array<uint8_t, 16> //
+unique_hash_key_algo_strong_sha256_fn<16>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+template std::array<uint8_t, 24> //
+unique_hash_key_algo_strong_sha256_fn<24>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+template std::array<uint8_t, 32> //
+unique_hash_key_algo_strong_sha256_fn<32>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+
+#if __has_include(<blake3.h>)
+
+template <size_t Size>
+std::array<uint8_t, Size>
+unique_hash_key_algo_strong_blake3_fn<Size>::operator()(
+    span<detail::unique_hash_key_item const> const in) const noexcept {
+  auto const& init = detail::unique_hash_key_algo_strong_blake3_init();
+  blake3_hasher h;
+  blake3_hasher_init_keyed(&h, init.key);
+  detail::unique_hash_key_hash_items(blake3_hasher_update, &h, in);
+  std::array<uint8_t, Size> out{};
+  blake3_hasher_finalize(&h, out.data(), out.size());
+  return out;
+}
+
+template std::array<uint8_t, 8> //
+unique_hash_key_algo_strong_blake3_fn<8>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+template std::array<uint8_t, 16> //
+unique_hash_key_algo_strong_blake3_fn<16>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+template std::array<uint8_t, 24> //
+unique_hash_key_algo_strong_blake3_fn<24>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+template std::array<uint8_t, 32> //
+unique_hash_key_algo_strong_blake3_fn<32>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+
+#if __has_include(<xxh3.h>)
+
+template <size_t Size>
+std::array<uint8_t, Size> unique_hash_key_algo_fast_xxh3_fn<Size>::operator()(
+    span<detail::unique_hash_key_item const> const in) const noexcept {
+  using ops = detail::unique_hash_key_algo_fast_xxh3_ops<Size>;
+  auto const& init = detail::unique_hash_key_algo_fast_xxh3_init();
+  XXH3_state_t h;
+  // ideally, XXH3_{N}bits_reset_withSecretAndSeed - but it may not be available
+  // so we must mimic it with XXH3_{N}bits_reset_withSecret and then setting the
+  // seed directly
+  ops::init(&h, init.secret, sizeof(init.secret));
+  h.seed = init.seed;
+  h.useSeed = true;
+  detail::unique_hash_key_hash_items(ops::update, &h, in);
+  auto v = ops::finalize(&h);
+  static_assert(sizeof(v) == Size);
+  std::array<uint8_t, Size> out{};
+  std::memcpy(out.data(), &v, sizeof(v));
+  return out;
+}
+
+template std::array<uint8_t, 8> //
+unique_hash_key_algo_fast_xxh3_fn<8>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+template std::array<uint8_t, 16> //
+unique_hash_key_algo_fast_xxh3_fn<16>::operator()(
+    span<detail::unique_hash_key_item const> in) const noexcept;
+
+#endif // __has_include(<xxh3.h>)
+
+#endif // __has_include(<blake3.h>)
+
+} // namespace folly
diff --git a/folly/folly/hash/UniqueHashKey.h b/folly/folly/hash/UniqueHashKey.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/hash/UniqueHashKey.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <string_view>
+#include <tuple>
+#include <type_traits>
+
+#include <folly/container/span.h>
+
+namespace folly {
+
+namespace detail {
+
+struct unique_hash_key_item {
+  static inline constexpr size_t len_include_mask = ~(~size_t(0) >> 1);
+
+  uint8_t const* buf{};
+  size_t len{}; // hi bit set means include len in hash
+
+  std::pair<size_t, bool> unpack_len() const {
+    constexpr auto mask = len_include_mask;
+    return {len & ~mask, len & mask};
+  }
+};
+
+} // namespace detail
+
+/// unique_hash_key_algo_strong_sha256_fn
+///
+/// Hashes span<detail::unique_hash_key_item const> using SHA256 with a key that
+/// may vary between processes.
+///
+/// The size must be in {8, 16, 24, 32}.
+///
+/// For use with unique_hash_key.
+template <size_t Size>
+struct unique_hash_key_algo_strong_sha256_fn {
+  static_assert(Size <= 32);
+  static_assert(Size % 8 == 0);
+  std::array<uint8_t, Size> operator()(
+      span<detail::unique_hash_key_item const> in) const noexcept;
+};
+template <size_t Size>
+inline constexpr unique_hash_key_algo_strong_sha256_fn<Size>
+    unique_hash_key_algo_strong_sha256{};
+
+#if __has_include(<blake3.h>)
+
+/// unique_hash_key_algo_strong_blake3_fn
+///
+/// Hashes span<detail::unique_hash_key_item const> using BLAKE3 with a key that
+/// may vary between processes.
+///
+/// The size must be in {8, 16, 24, 32}.
+///
+/// For use with unique_hash_key.
+template <size_t Size>
+struct unique_hash_key_algo_strong_blake3_fn {
+  static_assert(Size <= 32);
+  static_assert(Size % 8 == 0);
+  std::array<uint8_t, Size> operator()(
+      span<detail::unique_hash_key_item const> in) const noexcept;
+};
+template <size_t Size>
+inline constexpr unique_hash_key_algo_strong_blake3_fn<Size>
+    unique_hash_key_algo_strong_blake3{};
+
+#if __has_include(<xxh3.h>)
+
+/// unique_hash_key_algo_fast_xxh3_fn
+///
+/// Hashes span<detail::unique_hash_key_item const> using XXH3 with a key that
+/// may vary between processes.
+///
+/// The size must be in {8, 16}, corresponding to XXH3_64bits and XXH3_128bits.
+///
+/// For use with unique_hash_key.
+template <size_t Size>
+struct unique_hash_key_algo_fast_xxh3_fn {
+  static_assert(Size <= 16);
+  static_assert(Size % 8 == 0);
+  std::array<uint8_t, Size> operator()(
+      span<detail::unique_hash_key_item const> in) const noexcept;
+};
+template <size_t Size>
+inline constexpr unique_hash_key_algo_fast_xxh3_fn<Size>
+    unique_hash_key_algo_fast_xxh3{};
+
+#endif // __has_include(<xxh3.h>)
+
+#endif // __has_include(<blake3.h>)
+
+template <auto Algo>
+constexpr size_t unique_hash_key_algo_size_v =
+    decltype(Algo(span<detail::unique_hash_key_item const>{})){}.size();
+
+/// unique_hash_key
+///
+/// A unique hashtable key cryptographically hashed from input data.
+///
+/// The size must be in {8, 16, 24, 32}.
+///
+/// Uniqueness is only guaranteed between two constructions when all of these
+/// conditions hold:
+/// * At sufficiently large sizes, likely at least size 16.
+/// * For cryptographically strong hash algorithms, such as BLAKE3.
+/// * With equivalent hash algorithm objects of the same type.
+/// * With packs of hashable arguments which have the same sequences of types.
+/// * With packs of hashable arguments that differ in at least one element.
+template <size_t Size>
+class unique_hash_key {
+ private:
+  using self = unique_hash_key;
+
+  static inline constexpr size_t data_size = Size;
+  static inline constexpr size_t data_align = alignof(size_t);
+
+  static_assert(data_size <= 32);
+  static_assert(data_size % 8 == 0);
+
+  using item_type = detail::unique_hash_key_item;
+  using data_type = std::array<uint8_t, Size>;
+
+  template <typename Algo>
+  static inline constexpr bool is_algo_v =
+      std::is_invocable_v<Algo const&, span<item_type const>>;
+
+  alignas(data_align) data_type const data_;
+
+  template <
+      typename Int,
+      std::enable_if_t<is_non_bool_integral_v<Int>, int> = 0>
+  static item_type init_item(
+      Int const& arg,
+      [[maybe_unused]] size_t idx,
+      [[maybe_unused]] size_t size) {
+    auto const buf = reinterpret_cast<uint8_t const*>(&arg);
+    return {buf, sizeof(arg)};
+  }
+  static item_type init_item(std::string_view arg, size_t idx, size_t size) {
+    constexpr auto mask = item_type::len_include_mask;
+    auto const buf = reinterpret_cast<uint8_t const*>(arg.data());
+    auto const hi = mask * size_t(idx + 1 < size);
+    return {buf, arg.size() | hi};
+  }
+
+  template <typename Algo, typename... Arg>
+  static data_type init(Algo const algo, Arg const&... arg) noexcept {
+    constexpr auto len = sizeof...(Arg);
+    size_t idxr = 0;
+    size_t idxl = 0;
+    std::array<item_type, len> items{};
+    ((items[idxl++] = init_item(arg, idxr++, len)), ...);
+    return algo(items);
+  }
+
+  template <typename T, size_t E, typename V = std::remove_cv_t<T>>
+  static constexpr bool is_span_compatible_v = //
+      !std::is_volatile_v<T> && //
+      std::is_integral_v<V> && //
+      std::is_unsigned_v<V> && //
+      !std::is_same_v<bool, V> && //
+      !std::is_same_v<char, V> && //
+      alignof(V) <= data_align && //
+      (E == data_size / sizeof(T) || E == dynamic_extent);
+
+ public:
+  template <
+      typename Algo,
+      typename... Arg,
+      std::enable_if_t<is_algo_v<Algo>, int> = 0>
+  explicit unique_hash_key(Algo const algo, Arg const&... arg) noexcept
+      : data_{init(algo, arg...)} {
+    static_assert(sizeof(self) == Size);
+    static_assert(alignof(self) == alignof(size_t));
+  }
+
+  template <
+      typename T,
+      std::size_t E,
+      std::enable_if_t<is_span_compatible_v<T, E>, int> = 0>
+  explicit operator span<T const, E>() const noexcept {
+    constexpr auto count = E == dynamic_extent ? data_size / sizeof(T) : E;
+    return span<T const, E>{reinterpret_cast<T const*>(data_.data()), count};
+  }
+
+  friend auto operator==(self const& a, self const& b) noexcept {
+    return a.data_ == b.data_;
+  }
+  friend auto operator!=(self const& a, self const& b) noexcept {
+    return a.data_ != b.data_;
+  }
+};
+
+/// unique_hash_key_with
+///
+/// A unique-hash-key of the size returned by the given hash algorithm, using
+/// the given algorithm.
+///
+/// Hash values are unique to the process. But see the caveats regarding
+/// uniqueness above, with tuples replacing packs.
+///
+/// It is explicitly permitted to copy-construct unique_hash_key (base class)
+/// instances from instances of unique_hash_key_with<Algo> (derived class).
+/// There is no concern about object slicing.
+template <auto Algo>
+class unique_hash_key_with
+    : public unique_hash_key<unique_hash_key_algo_size_v<Algo>> {
+ private:
+  static constexpr size_t size = unique_hash_key_algo_size_v<Algo>;
+  using self = unique_hash_key_with;
+  using base = unique_hash_key<size>;
+
+  template <typename... Arg, size_t... Idx>
+  explicit unique_hash_key_with(
+      std::tuple<Arg...> const& arg, std::index_sequence<Idx...>)
+      : base(Algo, std::get<Idx>(arg)...) {}
+
+ public:
+  template <typename... Arg>
+  explicit unique_hash_key_with(std::tuple<Arg...> const& arg)
+      : self(arg, std::index_sequence_for<Arg...>{}) {}
+};
+
+/// unique_hash_key_strong_sha256
+///
+/// A unique-hash-key of the given size using the cryptographically strong
+/// SHA256 hash algorithm from OpenSSL.
+///
+/// Hash values are unique to the process. But see the caveats regarding
+/// uniqueness above, with tuples replacing packs.
+template <size_t Size>
+using unique_hash_key_strong_sha256 =
+    unique_hash_key_with<unique_hash_key_algo_strong_sha256<Size>>;
+
+#if __has_include(<blake3.h>)
+
+/// unique_hash_key_strong_blake3
+///
+/// A unique-hash-key of the given size using the cryptographically strong
+/// BLAKE3 hash algorithm.
+///
+/// Hash values are unique to the process. But see the caveats regarding
+/// uniqueness above, with tuples replacing packs.
+template <size_t Size>
+using unique_hash_key_strong_blake3 =
+    unique_hash_key_with<unique_hash_key_algo_strong_blake3<Size>>;
+
+#if __has_include(<xxh3.h>)
+
+/// unique_hash_key_strong_blake3
+///
+/// A unique-hash-key of the given size using the fast but not cryptographically
+/// strong XXH3 algorithm.
+///
+/// Hash values are unique to the process. But see the caveats regarding
+/// uniqueness above, with tuples replacing packs.
+template <size_t Size>
+using unique_hash_key_fast_xxh3 =
+    unique_hash_key_with<unique_hash_key_algo_fast_xxh3<Size>>;
+
+#endif // __has_include(<xxh3.h>)
+
+#endif // __has_include(<blake3.h>)
+
+} // namespace folly
+
+namespace std {
+
+template <size_t Size>
+struct hash<::folly::unique_hash_key<Size>> {
+  using folly_is_avalanching = std::true_type;
+
+  size_t operator()(::folly::unique_hash_key<Size> const& key) const noexcept {
+    return ::folly::span<size_t const>{key}[0];
+  }
+};
+
+template <auto Algo>
+struct hash<::folly::unique_hash_key_with<Algo>>
+    : hash<::folly::unique_hash_key<
+          ::folly::unique_hash_key_algo_size_v<Algo>>> {};
+
+} // namespace std
diff --git a/folly/folly/hash/detail/ChecksumDetail.cpp b/folly/folly/hash/detail/ChecksumDetail.cpp
--- a/folly/folly/hash/detail/ChecksumDetail.cpp
+++ b/folly/folly/hash/detail/ChecksumDetail.cpp
@@ -115,7 +115,7 @@
 namespace folly {
 namespace detail {
 
-#if FOLLY_SSE_PREREQ(4, 2)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 
 static __m128i crc32MulAdd(__m128i x, __m128i a, __m128i multiplier) {
   /*
diff --git a/folly/folly/hash/detail/ChecksumDetail.h b/folly/folly/hash/detail/ChecksumDetail.h
--- a/folly/folly/hash/detail/ChecksumDetail.h
+++ b/folly/folly/hash/detail/ChecksumDetail.h
@@ -18,7 +18,7 @@
 
 #include <folly/Portability.h>
 
-#if FOLLY_SSE_PREREQ(4, 2)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 #include <immintrin.h>
 #endif
 
@@ -99,7 +99,7 @@
 uint32_t crc32_hw(
     const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U);
 
-#if FOLLY_SSE_PREREQ(4, 2)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 uint32_t crc32_hw_aligned(
     uint32_t remainder, const __m128i* p, size_t vec_count);
 #endif
diff --git a/folly/folly/hash/detail/Crc32CombineDetail.cpp b/folly/folly/hash/detail/Crc32CombineDetail.cpp
--- a/folly/folly/hash/detail/Crc32CombineDetail.cpp
+++ b/folly/folly/hash/detail/Crc32CombineDetail.cpp
@@ -68,7 +68,7 @@
 
 } // namespace
 
-#if FOLLY_SSE_PREREQ(4, 2)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 
 // Reduction taken from
 // https://www.nicst.de/crc.pdf
@@ -120,7 +120,7 @@
   return 0;
 }
 
-#endif // FOLLY_SSE_PREREQ(4, 2)
+#endif // FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 
 static constexpr uint32_t crc32c_m = 0x82f63b78;
 static constexpr uint32_t crc32_m = 0xedb88320;
diff --git a/folly/folly/hash/detail/Crc32cDetail.cpp b/folly/folly/hash/detail/Crc32cDetail.cpp
--- a/folly/folly/hash/detail/Crc32cDetail.cpp
+++ b/folly/folly/hash/detail/Crc32cDetail.cpp
@@ -41,7 +41,7 @@
 namespace folly {
 namespace detail {
 
-#if defined(FOLLY_X64) && FOLLY_SSE_PREREQ(4, 2)
+#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)
 
 namespace crc32_detail {
 
diff --git a/folly/folly/hash/detail/RandomSeed.h b/folly/folly/hash/detail/RandomSeed.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/hash/detail/RandomSeed.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <cstdlib>
+
+#include <folly/Portability.h>
+
+namespace folly {
+namespace hash {
+namespace detail {
+
+// A non-deterministic seed to prevent users depend on particular values of the
+// hash. Based on the same idea as Google's Abseil implementation [1]:
+// leverage ASLR (Address Space Layout Randomization) [2] to get a per-run
+// random value.
+//
+// [1]:
+// https://github.com/abseil/abseil-cpp/blob/76fd1e96c71ad20fe08f1b7d18c6c55e197df85e/absl/hash/internal/hash.h#L1299-L1323
+// [2]: https://en.wikipedia.org/wiki/Address_space_layout_randomization
+class RandomSeed {
+ public:
+  static uint64_t seed() {
+    // Abseil's implementation takes an address of static variable. It works
+    // fine, as long as hash values doesn't cross shared object/binary
+    // boundary. Unfortunately, we observed few such cases, so we are taking
+    // address of `std::abort` instead to increase chances of having single
+    // address across multiple shared objects.
+    return kIsDebug
+        ? static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&std::abort))
+        : 0ULL;
+  }
+};
+
+} // namespace detail
+} // namespace hash
+} // namespace folly
diff --git a/folly/folly/hash/traits.h b/folly/folly/hash/traits.h
--- a/folly/folly/hash/traits.h
+++ b/folly/folly/hash/traits.h
@@ -85,8 +85,7 @@
  */
 template <
     typename T,
-    template <typename U>
-    typename Hasher,
+    template <typename U> typename Hasher,
     typename... Dependencies>
 using enable_hasher_helper = detail::enable_hasher_helper_impl<
     T,
diff --git a/folly/folly/init/Init.cpp b/folly/folly/init/Init.cpp
--- a/folly/folly/init/Init.cpp
+++ b/folly/folly/init/Init.cpp
@@ -49,8 +49,7 @@
 #else
 [[noreturn]]
 #endif
-static void
-wrapped_abort() {
+static void wrapped_abort() {
   abort();
 }
 #endif
@@ -112,11 +111,11 @@
   SingletonVault::singleton()->destroyInstancesFinal();
 }
 
-void init(int* argc, char*** argv, bool removeFlags) {
+void unsafe_unscoped_init(int* argc, char*** argv, bool removeFlags) {
   initImpl(argc, argv, InitOptions{}.removeFlags(removeFlags));
 }
 
-void init(int* argc, char*** argv, InitOptions options) {
+void unsafe_unscoped_init(int* argc, char*** argv, InitOptions options) {
   initImpl(argc, argv, options);
 }
 
diff --git a/folly/folly/init/Init.h b/folly/folly/init/Init.h
--- a/folly/folly/init/Init.h
+++ b/folly/folly/init/Init.h
@@ -81,7 +81,7 @@
  *                     gflags passed on the command line
  * @param options      options
  */
-class FOLLY_NODISCARD Init {
+class [[nodiscard]] Init {
  public:
   // Force ctor & dtor out of line for better stack traces even with LTO.
   FOLLY_NOINLINE Init(int* argc, char*** argv, bool removeFlags = true);
@@ -96,9 +96,16 @@
   Init& operator=(Init&&) = delete;
 };
 
-[[deprecated("Use the RAII version Init")]] void init(
-    int* argc, char*** argv, bool removeFlags = true);
-[[deprecated("Use the RAII version Init")]] void init(
-    int* argc, char*** argv, InitOptions options);
+void unsafe_unscoped_init(int* argc, char*** argv, bool removeFlags = true);
+void unsafe_unscoped_init(int* argc, char*** argv, InitOptions options);
+
+[[deprecated("Use the RAII version Init")]] inline void init(
+    int* argc, char*** argv, bool removeFlags = true) {
+  unsafe_unscoped_init(argc, argv, removeFlags);
+}
+[[deprecated("Use the RAII version Init")]] inline void init(
+    int* argc, char*** argv, InitOptions options) {
+  unsafe_unscoped_init(argc, argv, std::move(options));
+}
 
 } // namespace folly
diff --git a/folly/folly/io/Cursor.h b/folly/folly/io/Cursor.h
--- a/folly/folly/io/Cursor.h
+++ b/folly/folly/io/Cursor.h
@@ -1454,8 +1454,8 @@
   bool maybeShared_;
 };
 
-typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
-typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
+using RWPrivateCursor = RWCursor<CursorAccess::PRIVATE>;
+using RWUnshareCursor = RWCursor<CursorAccess::UNSHARE>;
 
 /**
  * Append to the end of a buffer chain, growing the chain (by allocating new
@@ -1734,16 +1734,15 @@
    *
    * @methodset Writing
    */
-  void insert(std::unique_ptr<folly::IOBuf> buf) {
+  void insert(std::unique_ptr<folly::IOBuf> buf, bool pack = true) {
     if (buf) {
       queueCache_.queue()->append(
-          std::move(buf), /* pack */ true, /* allowTailReuse */ true);
+          std::move(buf), pack, /* allowTailReuse */ true);
     }
   }
 
-  void insert(const folly::IOBuf& buf) {
-    queueCache_.queue()->append(
-        buf, /* pack */ true, /* allowTailReuse */ true);
+  void insert(const folly::IOBuf& buf, bool pack = true) {
+    queueCache_.queue()->append(buf, pack, /* allowTailReuse */ true);
   }
 
   /**
diff --git a/folly/folly/io/FsUtil.cpp b/folly/folly/io/FsUtil.cpp
--- a/folly/folly/io/FsUtil.cpp
+++ b/folly/folly/io/FsUtil.cpp
@@ -103,8 +103,6 @@
   return L"0123456789abcdef";
 }
 
-#if __cpp_lib_filesystem >= 201703
-
 std_fs::path unique_path_fn::operator()(std_fs::path const& model) const {
   constexpr auto pin = std_fs::path::value_type('%');
   constexpr auto hex = hex_(pin);
@@ -122,8 +120,6 @@
   }
   return std::move(result);
 }
-
-#endif
 
 } // namespace fs
 } // namespace folly
diff --git a/folly/folly/io/FsUtil.h b/folly/folly/io/FsUtil.h
--- a/folly/folly/io/FsUtil.h
+++ b/folly/folly/io/FsUtil.h
@@ -16,18 +16,13 @@
 
 #pragma once
 
-#if __has_include(<filesystem>)
 #include <filesystem>
-#endif
-
 #include <boost/filesystem.hpp>
 
 namespace folly {
 namespace fs {
 
-#if __cpp_lib_filesystem >= 201703
 namespace std_fs = std::filesystem;
-#endif
 
 // Functions defined in this file are meant to extend the
 // boost::filesystem library; functions will be named according to boost's
@@ -73,16 +68,12 @@
  */
 path executable_path();
 
-#if __cpp_lib_filesystem >= 201703
-
 struct unique_path_fn {
   std_fs::path operator()(
       std_fs::path const& model = "%%%%-%%%%-%%%%-%%%%") const;
 };
 using std_fs_unique_path_fn = unique_path_fn;
 inline constexpr std_fs_unique_path_fn std_fs_unique_path;
-
-#endif
 
 } // namespace fs
 } // namespace folly
diff --git a/folly/folly/io/HugePages.cpp b/folly/folly/io/HugePages.cpp
--- a/folly/folly/io/HugePages.cpp
+++ b/folly/folly/io/HugePages.cpp
@@ -18,7 +18,6 @@
 
 #include <folly/io/HugePages.h>
 
-#include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
@@ -32,7 +31,6 @@
 #include <boost/regex.hpp>
 
 #include <folly/Conv.h>
-#include <folly/CppAttributes.h>
 #include <folly/Format.h>
 #include <folly/Range.h>
 #include <folly/String.h>
@@ -137,9 +135,6 @@
     bool operator()(const HugePageSize& a, size_t b) const {
       return a.size < b;
     }
-    bool operator()(size_t a, const HugePageSize& b) const {
-      return a < b.size;
-    }
   };
 
   // Read and parse /proc/mounts
@@ -149,7 +144,7 @@
   gen::byLine("/proc/mounts") | gen::eachAs<StringPiece>() |
       [&](StringPiece line) {
         parts.clear();
-        split(" ", line, parts);
+        split(' ', line, parts);
         // device path fstype options uid gid
         if (parts.size() != 6) {
           throw std::runtime_error("Invalid /proc/mounts line");
@@ -159,7 +154,7 @@
         }
 
         options.clear();
-        split(",", parts[3], options);
+        split(',', parts[3], options);
         size_t pageSize = defaultHugePageSize;
         // Search for the "pagesize" option, which must have a value
         for (auto& option : options) {
diff --git a/folly/folly/io/HugePages.h b/folly/folly/io/HugePages.h
--- a/folly/folly/io/HugePages.h
+++ b/folly/folly/io/HugePages.h
@@ -56,7 +56,7 @@
  * mount_point might be empty if no hugetlbfs file system is mounted for
  * that size.
  */
-typedef std::vector<HugePageSize> HugePageSizeVec;
+using HugePageSizeVec = std::vector<HugePageSize>;
 
 /**
  * Get list of supported huge page sizes and their mount points, if
diff --git a/folly/folly/io/IOBuf.cpp b/folly/folly/io/IOBuf.cpp
--- a/folly/folly/io/IOBuf.cpp
+++ b/folly/folly/io/IOBuf.cpp
@@ -404,7 +404,7 @@
     auto mallocSize = goodMallocSize(capacity);
     // round capacity to a multiple of 8
     size_t minSize = ((capacity + 7) & ~7) + sizeof(SharedInfo);
-    // if we do not have space for the overhead, allocate the mem separateley
+    // if we do not have space for the overhead, allocate the mem separately
     if (mallocSize < minSize) {
       auto* buf = checkedMalloc(mallocSize);
       return takeOwnership(SIZED_FREE, buf, mallocSize, 0, 0);
@@ -802,7 +802,7 @@
 
 unique_ptr<IOBuf> IOBuf::cloneOneImpl(std::pmr::memory_resource* mr) const {
   if (sharedInfo_) {
-    sharedInfo_->refcount.fetch_add(1, std::memory_order_acq_rel);
+    sharedInfo_->refcount.fetch_add(1, std::memory_order_relaxed);
   }
 
   auto [storage, mallocSize] = allocateStorage<HeapStorage>(mr);
@@ -832,7 +832,7 @@
 
 IOBuf IOBuf::cloneOneAsValue() const {
   if (sharedInfo_) {
-    sharedInfo_->refcount.fetch_add(1, std::memory_order_acq_rel);
+    sharedInfo_->refcount.fetch_add(1, std::memory_order_relaxed);
   }
   return IOBuf(
       InternalConstructor(), sharedInfo_, buf_, capacity_, data_, length_);
@@ -1421,23 +1421,16 @@
 }
 
 unique_ptr<IOBuf> IOBuf::wrapIov(const iovec* vec, size_t count) {
-  unique_ptr<IOBuf> result = nullptr;
+  IOBuf result{};
   for (size_t i = 0; i < count; ++i) {
     size_t len = vec[i].iov_len;
     void* data = vec[i].iov_base;
     if (len > 0) {
       auto buf = wrapBuffer(data, len);
-      if (!result) {
-        result = std::move(buf);
-      } else {
-        result->appendToChain(std::move(buf));
-      }
+      result.appendToChain(std::move(buf));
     }
   }
-  if (FOLLY_UNLIKELY(result == nullptr)) {
-    return create(0);
-  }
-  return result;
+  return result.isChained() ? result.pop() : create(0);
 }
 
 std::unique_ptr<IOBuf> IOBuf::takeOwnershipIov(
diff --git a/folly/folly/io/IOBuf.h b/folly/folly/io/IOBuf.h
--- a/folly/folly/io/IOBuf.h
+++ b/folly/folly/io/IOBuf.h
@@ -301,9 +301,9 @@
 
   enum class CombinedOption { DEFAULT, COMBINED, SEPARATE };
 
-  typedef ByteRange value_type;
-  typedef Iterator iterator;
-  typedef Iterator const_iterator;
+  using value_type = ByteRange;
+  using iterator = Iterator;
+  using const_iterator = Iterator;
 
   using FreeFunction = void (*)(void* buf, void* userData);
 
@@ -1088,7 +1088,7 @@
    *
    * @post  headroom() >= minHeadroom
    * @post  tailroom() >= minTailroom
-   * @post  The contents between data() and tail() are preseved
+   * @post  The contents between data() and tail() are preserved
    *
    * @methodset Buffer Capacity
    */
@@ -1111,7 +1111,7 @@
    * Is this IOBuf part of a chain.
    *
    * Technically, all IOBufs are part of a chain, possibly of length 1. This
-   * functin checks if the chain is non-trivial, i.e. the chain has more than
+   * function checks if the chain is non-trivial, i.e. the chain has more than
    * just one IOBuf in it.
    *
    * @returns  true iff the the IOBuf's chain has more than 1 IOBuf in it
@@ -2238,8 +2238,8 @@
 
   template <class UniquePtr>
   struct UniquePtrDeleter : public DeleterBase {
-    typedef typename UniquePtr::pointer Pointer;
-    typedef typename UniquePtr::deleter_type Deleter;
+    using Pointer = typename UniquePtr::pointer;
+    using Deleter = typename UniquePtr::deleter_type;
 
     explicit UniquePtrDeleter(Deleter deleter) : deleter_(std::move(deleter)) {}
     void dispose(void* p) noexcept override {
diff --git a/folly/folly/io/IOBufQueue.cpp b/folly/folly/io/IOBufQueue.cpp
--- a/folly/folly/io/IOBufQueue.cpp
+++ b/folly/folly/io/IOBufQueue.cpp
@@ -158,7 +158,7 @@
 void IOBufQueue::append(
     const folly::IOBuf& buf, bool pack, bool allowTailReuse) {
   if (!head_ || !pack) {
-    append(buf.clone(), pack);
+    append(buf.clone(), pack, allowTailReuse);
     return;
   }
 
diff --git a/folly/folly/io/RecordIO.cpp b/folly/folly/io/RecordIO.cpp
--- a/folly/folly/io/RecordIO.cpp
+++ b/folly/folly/io/RecordIO.cpp
@@ -16,13 +16,9 @@
 
 #include <folly/io/RecordIO.h>
 
-#include <sys/types.h>
-
 #include <folly/Exception.h>
 #include <folly/FileUtil.h>
 #include <folly/Memory.h>
-#include <folly/Portability.h>
-#include <folly/ScopeGuard.h>
 #include <folly/String.h>
 #include <folly/portability/Unistd.h>
 
diff --git a/folly/folly/io/RecordIO.h b/folly/folly/io/RecordIO.h
--- a/folly/folly/io/RecordIO.h
+++ b/folly/folly/io/RecordIO.h
@@ -91,11 +91,11 @@
    * Note that the position includes the header, that is, it can be passed back
    * to seek().
    */
-  typedef Iterator iterator;
-  typedef Iterator const_iterator;
-  typedef std::pair<ByteRange, off_t> value_type;
-  typedef value_type& reference;
-  typedef const value_type& const_reference;
+  using iterator = Iterator;
+  using const_iterator = Iterator;
+  using value_type = std::pair<ByteRange, off_t>;
+  using reference = value_type&;
+  using const_reference = const value_type&;
 
   /**
    * A record reader with a fileId of 0 will return all records.
diff --git a/folly/folly/io/ShutdownSocketSet.cpp b/folly/folly/io/ShutdownSocketSet.cpp
--- a/folly/folly/io/ShutdownSocketSet.cpp
+++ b/folly/folly/io/ShutdownSocketSet.cpp
@@ -73,8 +73,9 @@
 
 ShutdownSocketSet::ShutdownSocketSet(size_t capacity)
     : capacity_(cap(capacity)),
-      data_(static_cast<relaxed_atomic<uint8_t>*>(
-          folly::checkedCalloc(capacity_, sizeof(relaxed_atomic<uint8_t>)))),
+      data_(
+          static_cast<relaxed_atomic<uint8_t>*>(folly::checkedCalloc(
+              capacity_, sizeof(relaxed_atomic<uint8_t>)))),
       nullFile_("/dev/null", O_RDWR) {}
 
 void ShutdownSocketSet::add(NetworkSocket fd) {
diff --git a/folly/folly/io/TypedIOBuf.h b/folly/folly/io/TypedIOBuf.h
--- a/folly/folly/io/TypedIOBuf.h
+++ b/folly/folly/io/TypedIOBuf.h
@@ -40,12 +40,12 @@
   static_assert(std::is_standard_layout<T>::value, "must be standard layout");
 
  public:
-  typedef T value_type;
-  typedef value_type& reference;
-  typedef const value_type& const_reference;
-  typedef uint32_t size_type;
-  typedef value_type* iterator;
-  typedef const value_type* const_iterator;
+  using value_type = T;
+  using reference = value_type&;
+  using const_reference = const value_type&;
+  using size_type = uint32_t;
+  using iterator = value_type*;
+  using const_iterator = const value_type*;
 
   explicit TypedIOBuf(IOBuf* buf) : buf_(buf) {}
 
diff --git a/folly/folly/io/async/AsyncBase.cpp b/folly/folly/io/async/AsyncBase.cpp
--- a/folly/folly/io/async/AsyncBase.cpp
+++ b/folly/folly/io/async/AsyncBase.cpp
@@ -21,13 +21,10 @@
 #include <stdexcept>
 #include <string>
 
-#include <boost/intrusive/parent_from_member.hpp>
 #include <glog/logging.h>
 
 #include <folly/Exception.h>
 #include <folly/Format.h>
-#include <folly/Likely.h>
-#include <folly/String.h>
 #include <folly/portability/Filesystem.h>
 #include <folly/portability/Unistd.h>
 
diff --git a/folly/folly/io/async/AsyncIoUringSocket.cpp b/folly/folly/io/async/AsyncIoUringSocket.cpp
--- a/folly/folly/io/async/AsyncIoUringSocket.cpp
+++ b/folly/folly/io/async/AsyncIoUringSocket.cpp
@@ -20,6 +20,7 @@
 #include <folly/io/async/AsyncIoUringSocket.h>
 #include <folly/io/async/AsyncSocket.h>
 #include <folly/io/async/IoUringEventBaseLocal.h>
+#include <folly/io/async/IoUringProvidedBufferRing.h>
 #include <folly/memory/Malloc.h>
 #include <folly/portability/SysUio.h>
 
@@ -99,7 +100,7 @@
     : evb_(evb), options_(std::move(options)) {
   backend_ = getBackendFromEventBase(evb);
 
-  if (!backend_->bufferProvider()) {
+  if (!backend_->hasBufferProvider()) {
     throw std::runtime_error("require a IoUringBackend with a buffer provider");
   }
   readSqe_ = ReadSqe::UniquePtr(new ReadSqe(this));
@@ -141,6 +142,7 @@
     : IoSqeBase(IoSqeBase::Type::Read), parent_(parent) {
   supportsMultishotRecv_ = parent->options_.multishotRecv &&
       parent->backend_->kernelSupportsRecvmsgMultishot();
+  useBundles_ = parent->options_.useBundles;
   // If the backend for this socket has an IoUringZeroCopyBufferPool, then zero
   // copy is enabled implicitly.
   supportsZeroCopyRx_ = parent->backend_->zcBufferPool() != nullptr;
@@ -150,7 +152,7 @@
 AsyncIoUringSocket::~AsyncIoUringSocket() {
   VLOG(3) << "~AsyncIoUringSocket() " << this;
 
-  // this is a bit unnecesary if we are already closed, but proper state
+  // this is a bit unnecessary if we are already closed, but proper state
   // tracking is coming later and will be easier to handle then
   closeNow();
 
@@ -202,7 +204,7 @@
   if (!io) {
     io = IoUringEventBaseLocal::try_get(eb);
   }
-  return io && io->bufferProvider() != nullptr;
+  return io && io->hasBufferProvider();
 }
 
 bool AsyncIoUringSocket::supportsZcRx(EventBase* eb) {
@@ -277,9 +279,11 @@
     // IP_BIND_ADDRESS_NO_PORT forces the OS to find a unique port relying
     // on only the local tuple. This limits the range of available ephemeral
     // ports.  Using the IP_BIND_ADDRESS_NO_PORT delays assigning a port until
-    // connect expanding the available port range.
+    // connect expanding the available port range, unless
+    // setBindAddressNoPort() is called.
     if (bindAddr.getPort() == 0) {
-      if (setSockOpt(IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, &one, sizeof(one))) {
+      if (bindAddressNoPort_ &&
+          setSockOpt(IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, &one, sizeof(one))) {
         auto errnoCopy = errno;
         callback->connectErr(AsyncSocketException(
             AsyncSocketException::NOT_OPEN,
@@ -390,10 +394,10 @@
   connectSqe_.reset();
   connectEndTime_ = std::chrono::steady_clock::now();
   if (res == 0) {
+    setStateEstablished();
     if (connectCallback_) {
       connectCallback_->connectSuccess();
     }
-    setStateEstablished();
   } else {
     state_ = State::Error;
     if (connectCallback_) {
@@ -450,7 +454,7 @@
 void AsyncIoUringSocket::readError() {
   VLOG(4) << " AsyncIoUringSocket::readError() this=" << this;
   state_ = State::Error;
-  failAllWrites();
+  shutdownFlags_ |= ShutFlags_Read | ShutFlags_Write;
 }
 
 void AsyncIoUringSocket::setReadCB(ReadCallback* callback) {
@@ -540,9 +544,8 @@
       << " inflight=" << inFlight() << " good_=" << parent_->good()
       << " submitNow=" << submitNow;
 
-  if (callback == readCallback_) {
-    // copied from AsyncSocket
-    VLOG(9) << "cb the same";
+  if (callback == readCallback_ && (!submitNow || inFlight())) {
+    VLOG(9) << "cb the same, skipping";
     return;
   }
   setReadCbCount_++;
@@ -613,14 +616,13 @@
           << " has_buffer=" << !!(flags & IORING_CQE_F_BUFFER)
           << " bytes_received=" << bytesReceived_;
   DestructorGuard dg(this);
-  auto buffer_guard = makeGuard([&, bp = lastUsedBufferProvider_] {
-    if (flags & IORING_CQE_F_BUFFER) {
-      DCHECK(bp);
-      if (bp) {
-        bp->unusedBuf(flags >> 16);
-      }
-    }
+  bool hasMore = (flags & IORING_CQE_F_BUF_MORE) != 0;
+  auto buffer_guard = makeGuard([&] {
+    CHECK(!(flags & IORING_CQE_F_BUFFER))
+        << "Buffer guard invoked but IORING_CQE_F_BUFFER is set! " << "flags=0x"
+        << std::hex << flags << " res=" << std::dec << res;
   });
+
   if (!readCallback_) {
     if (res == -ENOBUFS || res == -ECANCELED) {
       // ignore
@@ -631,8 +633,9 @@
       }
     } else if (res > 0 && lastUsedBufferProvider_) {
       // must take the buffer
+      uint16_t bufId = flags >> IORING_CQE_BUFFER_SHIFT;
       appendReadData(
-          lastUsedBufferProvider_->getIoBuf(flags >> 16, res),
+          lastUsedBufferProvider_->getIoBuf(bufId, res, hasMore),
           queuedReceivedData_);
       buffer_guard.dismiss();
     }
@@ -653,7 +656,12 @@
       }
     } else if (res < 0) {
       // assume ECANCELED is not an unrecoverable error state, but we do still
-      // have to propogate to the callback as they presumably called the cancel.
+      // have to propagate to the callback as they presumably called the cancel.
+      auto callback = readCallback_;
+      if (parent_ && res != -ECANCELED) {
+        readCallback_ = nullptr;
+        parent_->readError();
+      }
       AsyncSocketException::AsyncSocketExceptionType err;
       std::string error;
       switch (res) {
@@ -671,9 +679,9 @@
               ")");
           break;
       }
-      readCallback_->readErr(AsyncSocketException(err, std::move(error)));
+      callback->readErr(AsyncSocketException(err, error));
       if (parent_ && res != -ECANCELED) {
-        parent_->readError();
+        parent_->failAllWrites();
       }
     } else {
       uint64_t const cb_was = setReadCbCount_;
@@ -682,10 +690,13 @@
         const io_uring_zcrx_cqe* rcqe = (io_uring_zcrx_cqe*)(cqe + 1);
         auto pool = parent_->backend_->zcBufferPool();
         sendReadBuf(pool->getIoBuf(cqe, rcqe), queuedReceivedData_);
+        buffer_guard.dismiss();
       } else if (lastUsedBufferProvider_) {
+        auto bufId = flags >> 16;
         sendReadBuf(
-            lastUsedBufferProvider_->getIoBuf(flags >> 16, res),
+            lastUsedBufferProvider_->getIoBuf(bufId, res, hasMore),
             queuedReceivedData_);
+
         buffer_guard.dismiss();
       } else {
         // slow path as must have run out of buffers
@@ -764,6 +775,10 @@
           used_len = maxSize_;
         }
 
+        if (useBundles_) {
+          ioprio_flags |= IORING_RECVSEND_BUNDLE;
+        }
+
         ::io_uring_prep_recv(sqe, fd, nullptr, used_len, 0);
         sqe->buf_group = lastUsedBufferProvider_->gid();
         sqe->flags |= IOSQE_BUFFER_SELECT;
@@ -865,14 +880,14 @@
     WriteCallback* callback,
     std::unique_ptr<IOBuf>&& buf,
     WriteFlags flags,
-    bool zc)
+    ZeroCopyOptions options)
     : IoSqeBase(IoSqeBase::Type::Write),
       parent_(parent),
       callback_(callback),
       buf_(std::move(buf)),
       flags_(flags),
       totalLength_(0),
-      zerocopy_(zc) {
+      zcOptions_(options) {
   IOBuf const* p = buf_.get();
   do {
     if (auto l = p->length(); l > 0) {
@@ -914,11 +929,15 @@
     struct io_uring_sqe* sqe) noexcept {
   VLOG(5) << "write sqe submit " << this << " iovs=" << msg_.msg_iovlen
           << " length=" << totalLength_ << " ptr=" << msg_.msg_iov
-          << " zc=" << zerocopy_ << " fd = " << parent_->usedFd_
+          << " zc=" << zcOptions_.zeroCopy << " fd = " << parent_->usedFd_
           << " flags=" << parent_->mbFixedFileFlags_;
-  if (zerocopy_) {
+  if (zcOptions_.zeroCopy) {
     ::io_uring_prep_sendmsg_zc(
         sqe, parent_->usedFd_, &msg_, sendMsgFlags() | MSG_WAITALL);
+    if (zcOptions_.fixedBuf) {
+      sqe->ioprio |= IORING_RECVSEND_FIXED_BUF;
+      sqe->buf_index = zcOptions_.fixedBufIndex;
+    }
   } else {
     ::io_uring_prep_sendmsg(sqe, parent_->usedFd_, &msg_, sendMsgFlags());
   }
@@ -1165,6 +1184,9 @@
   readSqe_ = ReadSqe::UniquePtr(new ReadSqe(this));
   readSqe_->setReadCallback(oldReadCallback, false);
   readSqe_->setEventBase(nullptr);
+  SocketAddress remoteAddr;
+  getPeerAddress(&remoteAddr);
+  readSqe_->setUseZeroCopyRx(!remoteAddr.isLoopbackAddress());
 
   unregisterFd();
   if (!drc) {
@@ -1178,16 +1200,17 @@
     auto res = drc->prom.getSemiFuture();
     if (previous) {
       VLOG(4) << "Setting promise from previous and this one";
-      readSqe_->setOldEventBaseRead(std::move(*previous).deferValue(
-          [r = std::move(res)](
-              std::unique_ptr<folly::IOBuf>&& prevRes) mutable {
-            return std::move(r).deferValue(
-                [p = std::move(prevRes)](
-                    std::unique_ptr<folly::IOBuf>&& nextRes) mutable {
-                  p->appendToChain(std::move(nextRes));
-                  return std::move(p);
-                });
-          }));
+      readSqe_->setOldEventBaseRead(
+          std::move(*previous).deferValue(
+              [r = std::move(res)](
+                  std::unique_ptr<folly::IOBuf>&& prevRes) mutable {
+                return std::move(r).deferValue(
+                    [p = std::move(prevRes)](
+                        std::unique_ptr<folly::IOBuf>&& nextRes) mutable {
+                      p->appendToChain(std::move(nextRes));
+                      return std::move(p);
+                    });
+              }));
     } else {
       VLOG(4) << "Setting promise from this one";
       readSqe_->setOldEventBaseRead(std::move(res));
@@ -1274,7 +1297,7 @@
     shutdownWriteNow();
     return;
   }
-  if (state_ != State::Established) {
+  if (state_ != State::Established && !connecting()) {
     failAllWrites();
     return;
   }
@@ -1332,7 +1355,7 @@
   auto [promise, future] =
       makePromiseContract<std::vector<std::pair<int, uint32_t>>>();
   auto newSqe =
-      new WriteSqe(parent_, callback_, std::move(buf_), flags_, zerocopy_);
+      new WriteSqe(parent_, callback_, std::move(buf_), flags_, zcOptions_);
 
   // make sure to keep the state of where we are in the write
   newSqe->totalLength_ = totalLength_;
@@ -1537,7 +1560,12 @@
   if (!callback) {
     callback = &sNullWriteCallback;
   }
-  WriteSqe* w = new WriteSqe(this, callback, std::move(buf), flags, canzc);
+  WriteSqe::ZeroCopyOptions zcOptions{
+      .zeroCopy = canzc,
+      .fixedBuf = backend_->getArenaIndex() > 0,
+      .fixedBufIndex = 0,
+  };
+  WriteSqe* w = new WriteSqe(this, callback, std::move(buf), flags, zcOptions);
 
   VLOG(5) << "AsyncIoUringSocket::writeChain(" << this
           << " ) state=" << stateAsString() << " size=" << w->totalLength_
@@ -1794,7 +1822,7 @@
   return ::setsockopt(fd_.toFd(), level, optname, optval, optsize);
 }
 
-bool AsyncIoUringSocket::getTFOSucceded() const {
+bool AsyncIoUringSocket::getTFOSucceeded() const {
   return detail::tfo_succeeded(fd_);
 }
 
@@ -1821,15 +1849,10 @@
 void AsyncIoUringSocket::setFd(NetworkSocket ns) {
   fd_ = ns;
   try {
-    if (!backend_->kernelHasNonBlockWriteFixes()) {
-      // If the kernel doesnt have the fixes we have to disable the nonblock
-      // flag It will still be NONBLOCK as long as it goes through io_uring, but
-      // if we leave the flag then IO_URING will spin on some ops.
-      int flags =
-          ensureSocketReturnCode(fcntl(ns.toFd(), F_GETFL, 0), "get flags");
-      flags = flags & ~O_NONBLOCK;
-      ensureSocketReturnCode(fcntl(ns.toFd(), F_SETFL, flags), "set flags");
-    }
+    int flags =
+        ensureSocketReturnCode(fcntl(ns.toFd(), F_GETFL, 0), "get flags");
+    flags = flags & ~O_NONBLOCK;
+    ensureSocketReturnCode(fcntl(ns.toFd(), F_SETFL, flags), "set flags");
     registerFd();
   } catch (std::exception const& e) {
     LOG(ERROR) << "unable to setFd " << ns.toFd() << " : " << e.what();
diff --git a/folly/folly/io/async/AsyncIoUringSocket.h b/folly/folly/io/async/AsyncIoUringSocket.h
--- a/folly/folly/io/async/AsyncIoUringSocket.h
+++ b/folly/folly/io/async/AsyncIoUringSocket.h
@@ -51,9 +51,10 @@
 } // namespace folly
 
 #if FOLLY_HAS_LIBURING
-class IoUringBackend;
 
 namespace folly {
+class IoUringBackend;
+class IoUringProvidedBufferRing;
 
 class AsyncIoUringSocket : public AsyncSocketTransport {
  public:
@@ -61,12 +62,14 @@
   struct Options {
     Options()
         : allocateNoBufferPoolBuffer(defaultAllocateNoBufferPoolBuffer),
-          multishotRecv(true) {}
+          multishotRecv(true),
+          useBundles(false) {}
 
     static std::unique_ptr<IOBuf> defaultAllocateNoBufferPoolBuffer();
     folly::Function<std::unique_ptr<IOBuf>()> allocateNoBufferPoolBuffer;
     folly::Optional<AsyncWriter::ZeroCopyEnableFunc> zeroCopyEnable;
     bool multishotRecv;
+    bool useBundles;
   };
 
   using UniquePtr = std::unique_ptr<AsyncIoUringSocket, Destructor>;
@@ -108,6 +111,12 @@
     return connectEndTime_ - connectStartTime_;
   }
 
+  /*
+   * This flag controls whether or not IP_BIND_ADDRESS_NO_PORT is enabled for
+   * AsyncSocket sockets. This is enabled by default.
+   */
+  void setBindAddressNoPort(bool flag) { bindAddressNoPort_ = flag; }
+
   // AsyncSocketBase
   EventBase* getEventBase() const override { return evb_; }
 
@@ -226,7 +235,7 @@
 
   void asyncDetachFd(AsyncDetachFdCallback* callback);
   bool readSqeInFlight() const { return readSqe_->inFlight(); }
-  bool getTFOSucceded() const override;
+  bool getTFOSucceeded() const override;
   void enableTFO() override {
     // No-op if folly does not allow tfo
 #if FOLLY_ALLOW_TFO
@@ -248,14 +257,14 @@
   void unregisterFd();
   void readProcessSubmit(
       struct io_uring_sqe* sqe,
-      IoUringBufferProviderBase* bufferProvider,
+      IoUringProvidedBufferRing* bufferProvider,
       size_t* maxSize,
-      IoUringBufferProviderBase* usedBufferProvider) noexcept;
+      IoUringProvidedBufferRing* usedBufferProvider) noexcept;
   void readCallback(
       int res,
       uint32_t flags,
       size_t maxSize,
-      IoUringBufferProviderBase* bufferProvider) noexcept;
+      IoUringProvidedBufferRing* bufferProvider) noexcept;
   void allowReads();
   void previousReadDone();
   void processWriteQueue() noexcept;
@@ -336,7 +345,7 @@
 
     bool isEOF(const io_uring_cqe* cqe) noexcept;
 
-    IoUringBufferProviderBase* lastUsedBufferProvider_;
+    IoUringProvidedBufferRing* lastUsedBufferProvider_;
     ReadCallback* readCallback_ = nullptr;
     AsyncIoUringSocket* parent_;
     size_t maxSize_;
@@ -350,6 +359,7 @@
         false; // todo: this can be per process instead of per socket
     bool supportsZeroCopyRx_ = false;
     bool useZeroCopyRx_ = false;
+    bool useBundles_ = false;
 
     folly::Optional<folly::SemiFuture<std::unique_ptr<IOBuf>>>
         oldEventBaseRead_;
@@ -376,12 +386,17 @@
   using write_sqe_hook =
       boost::intrusive::list_base_hook<boost::intrusive::tag<write_sqe_tag>>;
   struct WriteSqe final : IoSqeBase, public write_sqe_hook {
+    struct ZeroCopyOptions {
+      bool zeroCopy{false};
+      bool fixedBuf{false};
+      uint16_t fixedBufIndex{0};
+    };
     explicit WriteSqe(
         AsyncIoUringSocket* parent,
         WriteCallback* callback,
         std::unique_ptr<IOBuf>&& buf,
         WriteFlags flags,
-        bool zc);
+        ZeroCopyOptions options);
     ~WriteSqe() override { VLOG(5) << "~WriteSqe() " << this; }
 
     void processSubmit(struct io_uring_sqe* sqe) noexcept override;
@@ -403,7 +418,7 @@
     size_t totalLength_;
     struct msghdr msg_;
 
-    bool zerocopy_{false};
+    ZeroCopyOptions zcOptions_;
     int refs_ = 1;
     folly::Function<bool(int, uint32_t)> detachedSignal_;
   };
@@ -500,6 +515,7 @@
   std::chrono::milliseconds connectTimeout_{0};
   std::chrono::steady_clock::time_point connectStartTime_;
   std::chrono::steady_clock::time_point connectEndTime_;
+  bool bindAddressNoPort_{true};
 
   // stopTLS helpers:
   std::string securityProtocol_;
diff --git a/folly/folly/io/async/AsyncIoUringSocketFactory.cpp b/folly/folly/io/async/AsyncIoUringSocketFactory.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/io/async/AsyncIoUringSocketFactory.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/io/async/AsyncIoUringSocketFactory.h>
+
+#include <folly/io/async/IoUringBackend.h>
+
+namespace folly {
+
+bool AsyncIoUringSocketFactory::bindSocketForZcRx(
+    AsyncSocketTransport& transport,
+    const folly::IPAddress& destAddr,
+    uint16_t destPort) {
+#if FOLLY_HAS_LIBURING
+  auto* backend = dynamic_cast<folly::IoUringBackend*>(
+      transport.getEventBase()->getBackend());
+  if (backend == nullptr) {
+    LOG(ERROR) << "bindSocketForZcRx: failed to get IoUringBackend";
+    return false;
+  }
+
+  int srcPort = backend->computeSrcPortForQueueId(destAddr, destPort);
+#else
+  int srcPort = -1;
+#endif
+
+  if (srcPort == -1) {
+    LOG(ERROR) << "bindSocketForZcRx: failed to resolve src port for destAddr="
+               << destAddr << ", destPort=" << destPort;
+    return false;
+  }
+
+  SocketAddress bindAddr(transport.getLocalAddress().getAddressStr(), srcPort);
+  sockaddr_storage storage{};
+  bindAddr.getAddress(&storage);
+
+  int fd = transport.getNetworkSocket().toFd();
+  if (::bind(
+          fd,
+          reinterpret_cast<const sockaddr*>(&storage),
+          bindAddr.getActualSize()) < 0) {
+    LOG(ERROR) << "bindSocketForZcRx: failed to bind socket, errno=" << errno
+               << ", srcPort=" << srcPort;
+    return false;
+  }
+  return true;
+}
+
+} // namespace folly
diff --git a/folly/folly/io/async/AsyncIoUringSocketFactory.h b/folly/folly/io/async/AsyncIoUringSocketFactory.h
--- a/folly/folly/io/async/AsyncIoUringSocketFactory.h
+++ b/folly/folly/io/async/AsyncIoUringSocketFactory.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <folly/io/async/AsyncIoUringSocket.h>
+#include <folly/io/async/AsyncSocketTransport.h>
 #include <folly/io/async/Liburing.h>
 
 namespace folly {
@@ -62,6 +63,16 @@
 
     return false;
   }
+
+  /**
+   * Bind an existing async transport socket to a source port for partial
+   * Toeplitz hash collisions. This should be called after socket creation but
+   * before connect().
+   */
+  static bool bindSocketForZcRx(
+      AsyncSocketTransport& transport,
+      const folly::IPAddress& destAddr,
+      uint16_t destPort);
 };
 
 } // namespace folly
diff --git a/folly/folly/io/async/AsyncPipe.cpp b/folly/folly/io/async/AsyncPipe.cpp
--- a/folly/folly/io/async/AsyncPipe.cpp
+++ b/folly/folly/io/async/AsyncPipe.cpp
@@ -91,24 +91,27 @@
       } catch (const std::exception& ex) {
         AsyncSocketException aex(
             AsyncSocketException::BAD_ARGS,
-            string("ReadCallback::getReadBuffer() "
-                   "threw exception: ") +
+            string(
+                "ReadCallback::getReadBuffer() "
+                "threw exception: ") +
                 ex.what());
         failRead(aex);
         return;
       } catch (...) {
         AsyncSocketException aex(
             AsyncSocketException::BAD_ARGS,
-            string("ReadCallback::getReadBuffer() "
-                   "threw non-exception type"));
+            string(
+                "ReadCallback::getReadBuffer() "
+                "threw non-exception type"));
         failRead(aex);
         return;
       }
       if (buf == nullptr || buflen == 0) {
         AsyncSocketException aex(
             AsyncSocketException::INVALID_STATE,
-            string("ReadCallback::getReadBuffer() "
-                   "returned empty buffer"));
+            string(
+                "ReadCallback::getReadBuffer() "
+                "returned empty buffer"));
         failRead(aex);
         return;
       }
diff --git a/folly/folly/io/async/AsyncSSLSocket.cpp b/folly/folly/io/async/AsyncSSLSocket.cpp
--- a/folly/folly/io/async/AsyncSSLSocket.cpp
+++ b/folly/folly/io/async/AsyncSSLSocket.cpp
@@ -54,7 +54,7 @@
 using folly::ssl::OpenSSLUtils;
 
 // We have one single dummy SSL context so that we can implement attach
-// and detach methods in a thread safe fashion without modifying opnessl.
+// and detach methods in a thread safe fashion without modifying openssl.
 SSLContext* dummyCtx = nullptr;
 SpinLock dummyCtxLock;
 
@@ -903,17 +903,7 @@
   sslState_ = STATE_CONNECTING;
   handshakeCallback_ = callback;
 
-  try {
-    ssl_.reset(ctx_->createSSL());
-  } catch (std::exception& e) {
-    sslState_ = STATE_ERROR;
-    static const Indestructible<AsyncSocketException> ex(
-        AsyncSocketException::INTERNAL_ERROR,
-        "error calling SSLContext::createSSL()");
-    LOG(ERROR) << "AsyncSSLSocket::sslConn(this=" << this << ", fd=" << fd_
-               << "): " << e.what();
-    return failHandshake(__func__, *ex);
-  }
+  createSSL();
 
   if (!encodedAlpn_.empty()) {
     int result = SSL_set_alpn_protos(
@@ -928,21 +918,6 @@
     }
   }
 
-  if (!setupSSLBio()) {
-    sslState_ = STATE_ERROR;
-    static const Indestructible<AsyncSocketException> ex(
-        AsyncSocketException::INTERNAL_ERROR, "error creating SSL bio");
-    return failHandshake(__func__, *ex);
-  }
-
-  if (!applyVerificationOptions(ssl_)) {
-    sslState_ = STATE_ERROR;
-    static const Indestructible<AsyncSocketException> ex(
-        AsyncSocketException::INTERNAL_ERROR,
-        "error applying the SSL verification options");
-    return failHandshake(__func__, *ex);
-  }
-
   // AsyncSSLSocket will leak memory if zero copy if left enabled after
   // the TLS handshake
   setZeroCopy(false);
@@ -956,7 +931,6 @@
     SSL_set_tlsext_host_name(ssl_.get(), tlsextHostname_.c_str());
   }
 
-  SSL_set_ex_data(ssl_.get(), getSSLExDataIndex(), this);
   sslSessionManager_.attachToSSL(ssl_.get());
 
   handshakeConnectTimeout_ = timeout;
@@ -1238,34 +1212,7 @@
   assert(state_ == StateEnum::ESTABLISHED && sslState_ == STATE_ACCEPTING);
   if (!ssl_) {
     /* lazily create the SSL structure */
-    try {
-      ssl_.reset(ctx_->createSSL());
-    } catch (std::exception& e) {
-      sslState_ = STATE_ERROR;
-      static const Indestructible<AsyncSocketException> ex(
-          AsyncSocketException::INTERNAL_ERROR,
-          "error calling SSLContext::createSSL()");
-      LOG(ERROR) << "AsyncSSLSocket::handleAccept(this=" << this
-                 << ", fd=" << fd_ << "): " << e.what();
-      return failHandshake(__func__, *ex);
-    }
-
-    if (!setupSSLBio()) {
-      sslState_ = STATE_ERROR;
-      static const Indestructible<AsyncSocketException> ex(
-          AsyncSocketException::INTERNAL_ERROR, "error creating write bio");
-      return failHandshake(__func__, *ex);
-    }
-
-    SSL_set_ex_data(ssl_.get(), getSSLExDataIndex(), this);
-
-    if (!applyVerificationOptions(ssl_)) {
-      sslState_ = STATE_ERROR;
-      static const Indestructible<AsyncSocketException> ex(
-          AsyncSocketException::INTERNAL_ERROR,
-          "error applying the SSL verification options");
-      return failHandshake(__func__, *ex);
-    }
+    createSSL();
   }
 
   if (server_ && parseClientHello_) {
@@ -1824,7 +1771,7 @@
     // up and written over multiple calls to sendmsg, we have to take care to
     // unset the EOR flag if it was included in the WriteFlags passed in and
     // we're writing a buffer that does _not_ contain the final byte of the
-    // orignally passed buffer.
+    // originally passed buffer.
     //
     // We handle EOR as follows:
     //   - We set currWriteFlags_ to the passed in WriteFlags.
@@ -1996,24 +1943,36 @@
 
   if (self->handshakeCallback_) {
     int callbackOk =
-        (self->handshakeCallback_->handshakeVer(self, preverifyOk, x509Ctx))
+        self->handshakeCallback_->handshakeVer(self, preverifyOk, x509Ctx)
         ? 1
         : 0;
 
     if (preverifyOk != callbackOk) {
       // HandshakeCB overwrites result from OpenSSL. One way or another, do not
-      // call CertificateIdentityVerifier.
+      // call verifyLeaf.
       return callbackOk;
     }
   }
 
+  // verifyContext can override the OpenSSL verification result. Unlike
+  // handshakeVer, it doesn't return early - allowing verifyLeaf to be called
+  // for the leaf certificate even if verifyContext changes the result.
+  if (self->certificateIdentityVerifier_) {
+    preverifyOk =
+        self->certificateIdentityVerifier_->verifyContext(preverifyOk, x509Ctx)
+        ? 1
+        : 0;
+  }
+
   if (!preverifyOk) {
-    // OpenSSL verification failure, no need to call CertificateIdentityVerifier
+    // Verification failed (either OpenSSL or verifyContext), no need to call
+    // verifyLeaf.
     return 0;
   }
 
-  // only invoke the CertificateIdentityVerifier for the leaf certificate and
-  // only if OpenSSL's preverify and the HandshakeCB's handshakeVer succeeded
+  // only invoke the verifyLeaf callback for the leaf certificate and
+  // only if all previous verification steps succeeded (OpenSSL, handshakeVer,
+  // and verifyContext)
 
   int currentDepth = X509_STORE_CTX_get_error_depth(x509Ctx);
   if (currentDepth != 0 || self->certificateIdentityVerifier_ == nullptr) {
@@ -2044,10 +2003,15 @@
   if (getSSLVersion() == SSL3_VERSION || getSSLVersion() == TLS1_VERSION) {
     // Socket timestamping can cause us to split up TLS records in a way that
     // breaks some old Android (<= 3.0) clients.
+
+    if (!byteEventHelper_) {
+      byteEventHelper_ = std::make_unique<ByteEventHelper>();
+    }
     return failByteEvents(AsyncSocketException(
         AsyncSocketException::NOT_SUPPORTED,
-        withAddr("failed to enable byte events: "
-                 "not supported for SSLv3 or TLSv1")));
+        withAddr(
+            "failed to enable byte events: "
+            "not supported for SSLv3 or TLSv1")));
   }
   AsyncSocket::enableByteEvents();
 }
@@ -2291,8 +2255,9 @@
     sigAlgs.append(
         folly::to<std::string>(clientHelloInfo_->clientHelloSigAlgs_[i].first));
     sigAlgs.push_back(',');
-    sigAlgs.append(folly::to<std::string>(
-        clientHelloInfo_->clientHelloSigAlgs_[i].second));
+    sigAlgs.append(
+        folly::to<std::string>(
+            clientHelloInfo_->clientHelloSigAlgs_[i].second));
   }
 
   return sigAlgs;
@@ -2350,6 +2315,43 @@
     return emptyAlpns;
   } else {
     return clientHelloInfo_->clientAlpns_;
+  }
+}
+
+void AsyncSSLSocket::createSSL() {
+  try {
+    ssl_.reset(ctx_->createSSL());
+  } catch (std::exception& e) {
+    sslState_ = STATE_ERROR;
+    static const Indestructible<AsyncSocketException> ex(
+        AsyncSocketException::INTERNAL_ERROR,
+        "error calling SSLContext::createSSL()");
+    LOG(ERROR) << "AsyncSSLSocket::createSSL(this=" << this << ", fd=" << fd_
+               << "): " << e.what();
+    return failHandshake(__func__, *ex);
+  }
+
+  if (!setupSSLBio()) {
+    sslState_ = STATE_ERROR;
+    static const Indestructible<AsyncSocketException> ex(
+        AsyncSocketException::INTERNAL_ERROR, "error creating SSL bio");
+    return failHandshake(__func__, *ex);
+  }
+
+  SSL_set_ex_data(ssl_.get(), getSSLExDataIndex(), this);
+
+  if (!applyVerificationOptions(ssl_)) {
+    sslState_ = STATE_ERROR;
+    static const Indestructible<AsyncSocketException> ex(
+        AsyncSocketException::INTERNAL_ERROR,
+        "error applying the SSL verification options");
+    return failHandshake(__func__, *ex);
+  }
+}
+
+void AsyncSSLSocket::ensureSSL() {
+  if (!ssl_) {
+    return createSSL();
   }
 }
 
diff --git a/folly/folly/io/async/AsyncSSLSocket.h b/folly/folly/io/async/AsyncSSLSocket.h
--- a/folly/folly/io/async/AsyncSSLSocket.h
+++ b/folly/folly/io/async/AsyncSSLSocket.h
@@ -75,7 +75,7 @@
  */
 class AsyncSSLSocket : public AsyncSocket {
  public:
-  typedef std::unique_ptr<AsyncSSLSocket, Destructor> UniquePtr;
+  using UniquePtr = std::unique_ptr<AsyncSSLSocket, Destructor>;
   using X509_deleter = folly::static_function_deleter<X509, &X509_free>;
 
   class HandshakeCB {
@@ -198,11 +198,13 @@
    * Struct to consolidate constructor arguments.
    */
   struct Options {
-    // If this verifier is set, it's used during the TLS handshake. It will be
-    // invoked to verify the peer's end-entity leaf certificate after OpenSSL's
-    // chain validation and after calling the HandshakeCB's handshakeVer() and
-    // only if these are successful.
-    std::shared_ptr<CertificateIdentityVerifier> verifier;
+    // If this verifier is set, it's used during the TLS handshake. First,
+    // verifyContext() is called during OpenSSL's certificate verification
+    // callback for each certificate in the chain, after HandshakeCB's
+    // handshakeVer() if set. Then, verifyLeaf() is invoked to verify the
+    // peer's end-entity leaf certificate, but only if OpenSSL's chain
+    // validation, handshakeVer(), and verifyContext() all succeeded.
+    std::shared_ptr<const CertificateIdentityVerifier> verifier;
     bool deferSecurityNegotiation{};
     bool isServer{};
     std::string serverName;
@@ -881,6 +883,8 @@
 
   const char* getNegotiatedGroup() const;
 
+  void ensureSSL();
+
  private:
   /**
    * Handle the return from invoking SSL_accept
@@ -978,6 +982,8 @@
 
   static void sslInfoCallback(const SSL* ssl, int where, int ret);
 
+  void createSSL();
+
   // Whether the current write to the socket should use MSG_MORE.
   bool corkCurrentWrite_{false};
   // SSL related members.
@@ -992,7 +998,8 @@
   std::shared_ptr<const folly::SSLContext> ctx_;
   // Callback for SSL_accept() or SSL_connect()
   HandshakeCB* handshakeCallback_{nullptr};
-  std::shared_ptr<CertificateIdentityVerifier> certificateIdentityVerifier_;
+  std::shared_ptr<const CertificateIdentityVerifier>
+      certificateIdentityVerifier_;
   ssl::SSLUniquePtr ssl_;
   Timeout handshakeTimeout_;
   Timeout connectionTimeout_;
diff --git a/folly/folly/io/async/AsyncServerSocket.cpp b/folly/folly/io/async/AsyncServerSocket.cpp
--- a/folly/folly/io/async/AsyncServerSocket.cpp
+++ b/folly/folly/io/async/AsyncServerSocket.cpp
@@ -54,6 +54,8 @@
     false;
 #endif // FOLLY_HAVE_MSG_ERRQUEUE
 
+static constexpr bool kSupportReflectTos = kIsLinuxActual;
+
 AsyncServerSocket::AcceptCallback::~AcceptCallback() = default;
 
 const uint32_t AsyncServerSocket::kDefaultMaxAcceptAtOnce;
@@ -287,7 +289,9 @@
 #if defined(__linux__)
     if (noTransparentTls_) {
       // Ignore return value, errors are ok
-      netops::setsockopt(fd, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0);
+      __u8 optval = FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED;
+      netops::setsockopt(
+          fd, SOL_SOCKET, FOLLY_SO_TTLS_TRUSTED, &optval, sizeof(optval));
     }
 #endif
 
@@ -341,7 +345,9 @@
 #if defined(__linux__)
   if (noTransparentTls_) {
     // Ignore return value, errors are ok
-    netops::setsockopt(fd, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0);
+    __u8 optval = FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED;
+    netops::setsockopt(
+        fd, SOL_SOCKET, FOLLY_SO_TTLS_TRUSTED, &optval, sizeof(optval));
   }
 #endif
 
@@ -507,7 +513,9 @@
 #if defined(__linux__)
     if (noTransparentTls_) {
       // Ignore return value, errors are ok
-      netops::setsockopt(s, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0);
+      __u8 optval = FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED;
+      netops::setsockopt(
+          s, SOL_SOCKET, FOLLY_SO_TTLS_TRUSTED, &optval, sizeof(optval));
     }
 #endif
 
@@ -837,7 +845,7 @@
  * TOS derived from the client's connect request
  */
 void AsyncServerSocket::setTosReflect(bool enable) {
-  if (!kIsLinux || !enable) {
+  if (!kSupportReflectTos || !enable) {
     tosReflect_ = false;
     return;
   }
@@ -861,7 +869,7 @@
 }
 
 void AsyncServerSocket::setListenerTos(uint32_t tos) {
-  if (!kIsLinux || tos == 0) {
+  if (!kSupportReflectTos || tos == 0) {
     listenerTos_ = 0;
     return;
   }
@@ -1040,7 +1048,7 @@
 
     // Connection accepted, get the SYN packet from the client if
     // TOS reflect is enabled
-    if (kIsLinux && clientSocket != NetworkSocket() && tosReflect_) {
+    if (kSupportReflectTos && clientSocket != NetworkSocket() && tosReflect_) {
       std::array<uint32_t, 64> buffer;
       socklen_t len = sizeof(buffer);
       int ret = netops::getsockopt(
diff --git a/folly/folly/io/async/AsyncServerSocket.h b/folly/folly/io/async/AsyncServerSocket.h
--- a/folly/folly/io/async/AsyncServerSocket.h
+++ b/folly/folly/io/async/AsyncServerSocket.h
@@ -47,10 +47,14 @@
 #define SO_REUSEPORT 15
 #endif
 
-#if defined __linux__ && !defined SO_NO_TRANSPARENT_TLS
-#define SO_NO_TRANSPARENT_TLS 200
+#if defined __linux__ && !defined FOLLY_SO_TTLS_TRUSTED
+#define FOLLY_SO_TTLS_TRUSTED 206
 #endif
 
+#if defined __linux__ && !defined FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED
+#define FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED 1
+#endif
+
 namespace folly {
 
 /**
@@ -73,7 +77,7 @@
  */
 class AsyncServerSocket : public DelayedDestruction, public AsyncSocketBase {
  public:
-  typedef std::unique_ptr<AsyncServerSocket, Destructor> UniquePtr;
+  using UniquePtr = std::unique_ptr<AsyncServerSocket, Destructor>;
   using CallbackAssignFunction =
       std::function<int(AsyncServerSocket*, NetworkSocket)>;
   // Disallow copy, move, and default construction.
@@ -188,7 +192,7 @@
      */
 
     // TODO(T81599451): Remove the acceptError(const std::exception&)
-    // after migration and remove compile warning supression.
+    // after migration and remove compile warning suppression.
     FOLLY_PUSH_WARNING
     FOLLY_GNU_DISABLE_WARNING("-Woverloaded-virtual")
     virtual void acceptError(exception_wrapper ew) noexcept {
diff --git a/folly/folly/io/async/AsyncSignalHandler.cpp b/folly/folly/io/async/AsyncSignalHandler.cpp
--- a/folly/folly/io/async/AsyncSignalHandler.cpp
+++ b/folly/folly/io/async/AsyncSignalHandler.cpp
@@ -63,8 +63,9 @@
   try {
     ev->eb_signal_set(signum, libeventCallback, this);
     if (ev->eb_event_base_set(eventBase_) != 0) {
-      throw std::runtime_error(folly::to<string>(
-          "error initializing event handler for signal ", signum));
+      throw std::runtime_error(
+          folly::to<string>(
+              "error initializing event handler for signal ", signum));
     }
 
     if (ev->eb_event_add(nullptr) != 0) {
@@ -80,10 +81,11 @@
 void AsyncSignalHandler::unregisterSignalHandler(int signum) {
   auto it = signalEvents_.find(signum);
   if (it == signalEvents_.end()) {
-    throw std::runtime_error(folly::to<string>(
-        "unable to unregister handler for signal ",
-        signum,
-        ": signal not registered"));
+    throw std::runtime_error(
+        folly::to<string>(
+            "unable to unregister handler for signal ",
+            signum,
+            ": signal not registered"));
   }
 
   it->second->eb_event_del();
diff --git a/folly/folly/io/async/AsyncSocket.cpp b/folly/folly/io/async/AsyncSocket.cpp
--- a/folly/folly/io/async/AsyncSocket.cpp
+++ b/folly/folly/io/async/AsyncSocket.cpp
@@ -25,7 +25,6 @@
 #include <boost/preprocessor/control/if.hpp>
 
 #include <folly/Exception.h>
-#include <folly/Format.h>
 #include <folly/Portability.h>
 #include <folly/SocketAddress.h>
 #include <folly/String.h>
@@ -33,6 +32,7 @@
 #include <folly/io/IOBuf.h>
 #include <folly/io/IOBufQueue.h>
 #include <folly/io/SocketOptionMap.h>
+#include <folly/io/async/IoUringBackend.h>
 #include <folly/lang/CheckedMath.h>
 #include <folly/portability/Fcntl.h>
 #include <folly/portability/Sockets.h>
@@ -58,7 +58,7 @@
   void put(ZeroCopyMemStore::Entry* /*entry*/) override {}
 };
 
-#if TCP_ZEROCOPY_RECEIVE
+#if defined(TCP_ZEROCOPY_RECEIVE)
 std::unique_ptr<folly::IOBuf> getRXZeroCopyIOBuf(
     ZeroCopyMemStore::EntryPtr&& ptr) {
   auto* entry = ptr.release();
@@ -147,6 +147,17 @@
 #else
 using ZeroCopyMMapMemStore = ZeroCopyMMapMemStoreFallback;
 #endif
+
+#if FOLLY_HAS_LIBURING
+bool checkIoUringBackend(folly::EventBase* evb) {
+  auto backend = dynamic_cast<folly::IoUringBackend*>(evb->getBackend());
+  return backend != nullptr && backend->supportAsyncSocket();
+}
+#else
+bool checkIoUringBackend(folly::EventBase*) {
+  return false;
+}
+#endif
 } // namespace
 
 #if FOLLY_HAVE_VLA
@@ -648,7 +659,9 @@
   if (noTransparentTls) {
     // Ignore return value, errors are ok
     VLOG(5) << "Disabling TTLS for fd " << fd;
-    netops::setsockopt(fd, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0);
+    __u8 optval = FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED;
+    netops::setsockopt(
+        fd, SOL_SOCKET, FOLLY_SO_TTLS_TRUSTED, &optval, sizeof(optval));
   }
   if (noTSocks) {
     VLOG(5) << "Disabling TSOCKS for fd " << fd;
@@ -775,6 +788,7 @@
 void AsyncSocket::init() {
   if (eventBase_) {
     eventBase_->dcheckIsInEventBaseThread();
+    useIoUring_ = checkIoUringBackend(eventBase_);
   }
   eventFlags_ = EventHandler::NONE;
   sendTimeout_ = 0;
@@ -789,6 +803,10 @@
   appBytesReceived_ = 0;
   totalAppBytesScheduledForWrite_ = 0;
   sendMsgParamCallback_ = &defaultSendMsgParamsCallback;
+  if (useIoUring_ && state_ == StateEnum::ESTABLISHED) {
+    assert(iouSendHandle_ == nullptr);
+    iouSendHandle_ = IoUringSendHandle::create(eventBase_, fd_, addr_, this);
+  }
 }
 
 AsyncSocket::~AsyncSocket() {
@@ -993,9 +1011,11 @@
       // IP_BIND_ADDRESS_NO_PORT forces the OS to find a unique port relying
       // on only the local tuple. This limits the range of available ephemeral
       // ports.  Using the IP_BIND_ADDRESS_NO_PORT delays assigning a port until
-      // connect expanding the available port range.
+      // connect expanding the available port range, unless
+      // setBindAddressNoPort() is called.
       if (bindAddr.getPort() == 0) {
-        if (netops_->setsockopt(
+        if (bindAddressNoPort_ &&
+            netops_->setsockopt(
                 fd_, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, &one, sizeof(one))) {
           auto errnoCopy = errno;
           doClose();
@@ -1044,6 +1064,12 @@
     // Perform the connect()
     address.getAddress(&addrStorage);
 
+    assert(eventBase_ != nullptr);
+    if (useIoUring_ && !iouSendHandle_) {
+      iouSendHandle_ =
+          IoUringSendHandle::create(eventBase_, fd_, address, this);
+    }
+
     if (tfoInfo_.enabled) {
       state_ = StateEnum::FAST_OPEN;
       tfoInfo_.attempted = true;
@@ -1074,9 +1100,18 @@
   VLOG(8) << "AsyncSocket::connect succeeded immediately; this=" << this;
   assert(errMessageCallback_ == nullptr);
   assert(readCallback_ == nullptr);
-  assert(writeReqHead_ == nullptr);
+  assert(!hasPendingWrites());
+  assert(iouConnectHandle_ == nullptr);
   if (state_ != StateEnum::FAST_OPEN) {
     state_ = StateEnum::ESTABLISHED;
+    if (useIoUring_ && netops_->set_socket_blocking(fd_)) {
+      auto errnoCopy = errno;
+      AsyncSocketException tex(
+          AsyncSocketException::INTERNAL_ERROR,
+          withAddr("failed to restore socket in blocking mode"),
+          errnoCopy);
+      return failConnect(__func__, tex);
+    }
   }
   invokeConnectSuccess();
 }
@@ -1086,8 +1121,13 @@
   if (rv < 0) {
     auto errnoCopy = errno;
     if (errnoCopy == EINPROGRESS) {
-      scheduleConnectTimeout();
-      registerForConnectEvents();
+      if (!useIoUring_) {
+        scheduleConnectTimeout();
+        registerForConnectEvents();
+      } else {
+        iouConnectHandle_ = IoUringConnectHandle::create(
+            eventBase_, fd_, this, connectTimeout_);
+      }
     } else {
       throw AsyncSocketException(
           AsyncSocketException::NOT_OPEN,
@@ -1173,12 +1213,13 @@
 }
 
 void AsyncSocket::setErrMessageCB(ErrMessageCallback* callback) {
+  CHECK(!useIoUring_);
   VLOG(6) << "AsyncSocket::setErrMessageCB() this=" << this << ", fd=" << fd_
           << ", callback=" << callback << ", state=" << state_;
 
   // In the latest stable kernel 4.14.3 as of 2017-12-04, unix domain
   // socket does not support MSG_ERRQUEUE. So recvmsg(MSG_ERRQUEUE)
-  // will read application data from unix doamin socket as error
+  // will read application data from unix domain socket as error
   // message, which breaks the message flow in application.  Feel free
   // to remove the next code block if MSG_ERRQUEUE is added for unix
   // domain socket in the future.
@@ -1242,6 +1283,7 @@
 }
 
 void AsyncSocket::setReadAncillaryDataCB(ReadAncillaryDataCallback* callback) {
+  CHECK(!useIoUring_);
   VLOG(6) << "AsyncSocket::setReadAncillaryDataCB() this=" << this << ", fd="
           << fd_ << ", callback=" << callback << ", state=" << state_;
 
@@ -1319,6 +1361,11 @@
       uint16_t oldFlags = eventFlags_;
       if (readCallback_) {
         eventFlags_ |= EventHandler::READ;
+        if (useIoUring_ && !iouRecvHandle_) {
+          iouRecvHandle_ =
+              IoUringRecvHandle::create(eventBase_, fd_, addr_, this);
+          CHECK(iouRecvHandle_);
+        }
       } else {
         eventFlags_ &= ~EventHandler::READ;
       }
@@ -1358,6 +1405,7 @@
 }
 
 bool AsyncSocket::setZeroCopy(bool enable) {
+  CHECK(!useIoUring_ || !enable);
   if (msgErrQueueSupported) {
     zeroCopyVal_ = enable;
 
@@ -1588,13 +1636,22 @@
 
   try {
 #if FOLLY_HAVE_SO_TIMESTAMPING
+    if (useIoUring_) {
+      throw AsyncSocketException(
+          AsyncSocketException::NOT_SUPPORTED,
+          withAddr(
+              "failed to enable byte events: "
+              "EVB is using io_uring"));
+    }
+
     // make sure we have a connected IP socket that supports error queues
     // (Unix sockets do not support error queues)
     if (NetworkSocket() == fd_ || !good()) {
       throw AsyncSocketException(
           AsyncSocketException::INVALID_STATE,
-          withAddr("failed to enable byte events: "
-                   "socket is not open or not in a good state"));
+          withAddr(
+              "failed to enable byte events: "
+              "socket is not open or not in a good state"));
     }
     folly::SocketAddress addr = {};
     try {
@@ -1604,8 +1661,9 @@
     } catch (const std::system_error&) {
       throw AsyncSocketException(
           AsyncSocketException::INVALID_STATE,
-          withAddr("failed to enable byte events: "
-                   "socket is not open or not in a good state"));
+          withAddr(
+              "failed to enable byte events: "
+              "socket is not open or not in a good state"));
     }
     const auto family = addr.getFamily();
     if (family != AF_INET && family != AF_INET6) {
@@ -1624,16 +1682,18 @@
       if (0 != ret) {
         throw AsyncSocketException(
             AsyncSocketException::INTERNAL_ERROR,
-            withAddr("failed to enable byte events: "
-                     "timestamps may not be supported for this socket type "
-                     "or socket be closed"),
+            withAddr(
+                "failed to enable byte events: "
+                "timestamps may not be supported for this socket type "
+                "or socket be closed"),
             getSockOptErrno);
       }
       if (0 != flags) {
         throw AsyncSocketException(
             AsyncSocketException::INTERNAL_ERROR,
-            withAddr("failed to enable byte events: "
-                     "timestamps may have already been enabled"),
+            withAddr(
+                "failed to enable byte events: "
+                "timestamps may have already been enabled"),
             getSockOptErrno);
       }
     }
@@ -1697,7 +1757,7 @@
     // [1]
     // https://github.com/torvalds/linux/commit/b534dc46c8ae0165b1b2509be24dbea4fa9c4011
 
-    while (byteEventsEnabledMaxAttempts++ < SO_MAX_ATTEMPTS_ENABLE_BYTEEVENTS) {
+    while (byteEventsEnabledMaxAttempts++ < kMaxAttemptsEnableByteEvents) {
       folly::TcpInfo::LookupOptions options = {};
       options.getMemInfo = true;
       const auto expectTInfoBefore = getTcpInfo(options);
@@ -1757,7 +1817,7 @@
       }
     }
 
-    if (byteEventsEnabledMaxAttempts > SO_MAX_ATTEMPTS_ENABLE_BYTEEVENTS) {
+    if (byteEventsEnabledMaxAttempts > kMaxAttemptsEnableByteEvents) {
       throw AsyncSocketException(
           AsyncSocketException::INTERNAL_ERROR,
           withAddr(
@@ -1874,10 +1934,11 @@
   bool mustRegister = false;
   if ((state_ == StateEnum::ESTABLISHED || state_ == StateEnum::FAST_OPEN) &&
       !connecting()) {
-    if (writeReqHead_ == nullptr) {
+    if (!hasPendingWrites()) {
       // If we are established and there are no other writes pending,
       // we can attempt to perform the write immediately.
       assert(writeReqTail_ == nullptr);
+      assert(iouSendHandle_ ? iouSendHandle_->empty() : true);
       assert((eventFlags_ & EventHandler::WRITE) == 0);
 
       callbackWithState.notifyOnWrite();
@@ -1933,33 +1994,53 @@
     return invalidState(callback);
   }
 
-  // Create a new WriteRequest to add to the queue
-  WriteRequest* req;
-  try {
-    req = BytesWriteRequest::newRequest(
-        this,
-        callbackWithState,
+  if (!useIoUring_) {
+    // Create a new WriteRequest to add to the queue
+    WriteRequest* req;
+    try {
+      req = BytesWriteRequest::newRequest(
+          this,
+          callbackWithState,
+          vec + countWritten,
+          uint32_t(count - countWritten),
+          partialWritten,
+          uint32_t(bytesWritten),
+          std::move(ioBuf),
+          flags);
+    } catch (const std::exception& ex) {
+      // we mainly expect to catch std::bad_alloc here
+      AsyncSocketException tex(
+          AsyncSocketException::INTERNAL_ERROR,
+          withAddr(string("failed to append new WriteRequest: ") + ex.what()));
+      return failWrite(__func__, callback, size_t(bytesWritten), tex);
+    }
+    req->consume();
+    if (writeReqTail_ == nullptr) {
+      assert(writeReqHead_ == nullptr);
+      writeReqHead_ = writeReqTail_ = req;
+    } else {
+      writeReqTail_->append(req);
+      writeReqTail_ = req;
+    }
+  } else {
+    auto cb = callback ? callback->getReleaseIOBufCallback() : nullptr;
+    if (ioBuf) {
+      for (uint32_t i = countWritten; i != 0; --i) {
+        assert(ioBuf);
+        auto next = ioBuf->pop();
+        releaseIOBuf(std::move(ioBuf), cb);
+        ioBuf = std::move(next);
+      }
+    }
+    iouSendHandle_->write(
+        callback,
         vec + countWritten,
-        uint32_t(count - countWritten),
+        count - countWritten,
         partialWritten,
-        uint32_t(bytesWritten),
+        bytesWritten,
         std::move(ioBuf),
         flags);
-  } catch (const std::exception& ex) {
-    // we mainly expect to catch std::bad_alloc here
-    AsyncSocketException tex(
-        AsyncSocketException::INTERNAL_ERROR,
-        withAddr(string("failed to append new WriteRequest: ") + ex.what()));
-    return failWrite(__func__, callback, size_t(bytesWritten), tex);
   }
-  req->consume();
-  if (writeReqTail_ == nullptr) {
-    assert(writeReqHead_ == nullptr);
-    writeReqHead_ = writeReqTail_ = req;
-  } else {
-    writeReqTail_->append(req);
-    writeReqTail_ = req;
-  }
 
   if (bufferCallback_) {
     bufferCallback_->onEgressBuffered();
@@ -2015,7 +2096,7 @@
   //
   // We only need to drain pending writes if we are still in STATE_CONNECTING
   // or STATE_ESTABLISHED
-  if ((writeReqHead_ == nullptr) ||
+  if ((!hasPendingWrites()) ||
       !(state_ == StateEnum::CONNECTING || state_ == StateEnum::ESTABLISHED)) {
     closeNow();
     return;
@@ -2060,6 +2141,11 @@
     eventBase_->dcheckIsInEventBaseThread();
   }
 
+  if (iouRecvHandle_) {
+    iouRecvHandle_->cancel();
+    iouRecvHandle_.reset();
+  }
+
   switch (state_) {
     case StateEnum::ESTABLISHED:
     case StateEnum::CONNECTING:
@@ -2090,6 +2176,13 @@
       }
 
       invokeConnectErr(getSocketClosedLocallyEx());
+      if (iouConnectHandle_) {
+        if (iouConnectHandle_->cancel()) {
+          iouConnectHandle_.release();
+        } else {
+          iouConnectHandle_.reset();
+        }
+      }
 
       failAllWrites(getSocketClosedLocallyEx());
 
@@ -2113,7 +2206,7 @@
       assert(eventFlags_ == EventHandler::NONE);
       assert(connectCallback_ == nullptr);
       assert(readCallback_ == nullptr);
-      assert(writeReqHead_ == nullptr);
+      assert(!hasPendingWrites());
       shutdownFlags_ |= (SHUT_READ | SHUT_WRITE);
       state_ = StateEnum::CLOSED;
       return;
@@ -2145,7 +2238,7 @@
 
   // If there are no pending writes, shutdownWrite() is identical to
   // shutdownWriteNow().
-  if (writeReqHead_ == nullptr) {
+  if (!hasPendingWrites()) {
     shutdownWriteNow();
     return;
   }
@@ -2312,6 +2405,19 @@
   eventBase->dcheckIsInEventBaseThread();
 
   eventBase_ = eventBase;
+  if (useIoUring_) {
+    if (eventFlags_ & EventHandler::READ) {
+      CHECK(iouRecvHandle_ != nullptr);
+      CHECK(readCallback_ != nullptr);
+      iouRecvHandle_ = IoUringRecvHandle::clone(
+          eventBase, fd_, addr_, this, std::move(iouRecvHandle_));
+    }
+    if (iouSendHandle_) {
+      iouSendHandle_ =
+          IoUringSendHandle::clone(eventBase, std::move(iouSendHandle_));
+    }
+  }
+
   ioHandler_.attachEventBase(eventBase);
 
   updateEventRegistration();
@@ -2347,6 +2453,17 @@
 
   eventBase_ = nullptr;
 
+  if (useIoUring_) {
+    if (iouRecvHandle_) {
+      iouRecvHandle_->detachEventBase();
+    }
+
+    if (iouSendHandle_) {
+      iouSendHandle_->update(EventHandler::NONE);
+      iouSendHandle_->detachEventBase();
+    }
+  }
+
   ioHandler_.unregisterHandler();
 
   ioHandler_.detachEventBase();
@@ -2429,7 +2546,7 @@
   *address = addr_;
 }
 
-bool AsyncSocket::getTFOSucceded() const {
+bool AsyncSocket::getTFOSucceeded() const {
   return detail::tfo_succeeded(fd_);
 }
 
@@ -2929,24 +3046,27 @@
           AsyncSocketException ex(
               AsyncSocketException::INTERNAL_ERROR,
               withAddr(
-                  string("AsyncSocket::handleErrMessages(), "
-                         "internal exception during ByteEvent processing: ") +
+                  string(
+                      "AsyncSocket::handleErrMessages(), "
+                      "internal exception during ByteEvent processing: ") +
                   behEx.what()));
           failByteEvents(ex);
         } catch (const std::exception& ex) {
           AsyncSocketException tex(
               AsyncSocketException::UNKNOWN,
-              string("AsyncSocket::handleErrMessages(), "
-                     "unhandled exception during ByteEvent processing, "
-                     "threw exception: ") +
+              string(
+                  "AsyncSocket::handleErrMessages(), "
+                  "unhandled exception during ByteEvent processing, "
+                  "threw exception: ") +
                   ex.what());
           failByteEvents(tex);
         } catch (...) {
           AsyncSocketException tex(
               AsyncSocketException::UNKNOWN,
-              string("AsyncSocket::handleErrMessages(), "
-                     "unhandled exception during ByteEvent processing, "
-                     "threw non-exception type"));
+              string(
+                  "AsyncSocket::handleErrMessages(), "
+                  "unhandled exception during ByteEvent processing, "
+                  "threw non-exception type"));
           failByteEvents(tex);
         }
       }
@@ -3082,7 +3202,7 @@
 }
 
 AsyncSocket::ReadCode AsyncSocket::processZeroCopyRead() {
-#if TCP_ZEROCOPY_RECEIVE
+#if defined(TCP_ZEROCOPY_RECEIVE)
   if (zerocopyReadDisabled_) {
     return ReadCode::READ_NOT_SUPPORTED;
   }
@@ -3098,8 +3218,10 @@
     VLOG(5) << "AsyncSocket::processZeroCopyRead() this=" << this
             << ", reading pre-received data";
 
+    auto len = preReceivedData_->computeChainDataLength();
     readCallback_->readZeroCopyDataAvailable(
         std::move(preReceivedData_), 0 /*additionalBytes*/);
+    appBytesReceived_ += len;
 
     return ReadCode::READ_DONE;
   }
@@ -3118,6 +3240,8 @@
 
   zc.address = reinterpret_cast<uint64_t>(ptr->data);
   zc.length = ptr->capacity;
+  assert(uintptr_t(zc.address) % sysconf(_SC_PAGESIZE) == 0);
+  assert(zc.length % sysconf(_SC_PAGESIZE) == 0);
   auto zc_length = zc.length;
 
   zc.copybuf_address = reinterpret_cast<__u64>(copybuf);
@@ -3133,9 +3257,11 @@
     // the kernel. Only once we have seen 0 bytes returned from the kernel do we
     // want to return the error to the caller.
     if (zc.err) {
-      zerocopyReadErr_ = zc.err;
+      zerocopyReadErr_ = -zc.err; // note: zc.err <= 0
     }
 
+    assert(zc.length % sysconf(_SC_PAGESIZE) == 0);
+
     auto len = zc.length + zc.copybuf_len;
 
     if (zerocopyReadErr_ && len == 0) {
@@ -3166,6 +3292,7 @@
 
     if (len) {
       readCallback_->readZeroCopyDataAvailable(std::move(buf), zc.copybuf_len);
+      appBytesReceived_ += len;
 
       // If we completely filled up the zerocopy buffer then we likely have
       // more data buffered in the kernel, so return READ_CONTINUE to try again.
@@ -3227,8 +3354,9 @@
   } catch (const std::exception& ex) {
     AsyncSocketException tex(
         AsyncSocketException::BAD_ARGS,
-        string("ReadCallback::getReadBuffer() "
-               "threw exception: ") +
+        string(
+            "ReadCallback::getReadBuffer() "
+            "threw exception: ") +
             ex.what());
     return failRead(__func__, tex);
   } catch (...) {
@@ -3252,7 +3380,7 @@
   // `performReadMsg` go through `recvmsg` (e.g., "pre-received data" and
   // "recv" are possibilities).  For those that do not, we want at a minimum
   // `msg_controllen` and `msg_flags` to be zero.
-  struct ::msghdr msg {};
+  struct ::msghdr msg{};
   // Dest address info
   msg.msg_name = nullptr;
   msg.msg_namelen = 0;
@@ -3292,7 +3420,7 @@
       }
       // `ancillaryData()` is expected to check and error on this, since
       // it's probably incorrect to process truncated ancillary data.  If
-      // some bizarro callback wants to treat this as recoverable, it can
+      // some bizarre callback wants to treat this as recoverable, it can
       // clear `MSG_CTRUNC` on `msg_flags` before returning.
       //
       // Don't move this: `performReadMsg` doesn't guarantee that `msg_flags`
@@ -3320,33 +3448,14 @@
     // No more data to read right now.
     return ReadCode::READ_DONE;
   } else if (bytesRead == READ_ERROR) {
-    readErr_ = READ_ERROR;
-    if (readResult.exception) {
-      return failRead(__func__, *readResult.exception);
-    }
     auto errnoCopy = errno;
-    AsyncSocketException ex(
-        AsyncSocketException::INTERNAL_ERROR,
-        withAddr("recv() failed"),
-        errnoCopy);
-    return failRead(__func__, ex);
+    recvErr(errnoCopy, std::move(readResult.exception));
+    return AsyncSocket::ReadCode::READ_DONE;
   } else if (bytesRead == READ_ASYNC) {
     return ReadCode::READ_DONE;
   } else {
     assert(bytesRead == READ_EOF);
-    readErr_ = READ_EOF;
-    // EOF
-    shutdownFlags_ |= SHUT_READ;
-    if (!updateEventRegistration(0, EventHandler::READ)) {
-      // we've already been moved into STATE_ERROR
-      assert(state_ == StateEnum::ERROR);
-      assert(readCallback_ == nullptr);
-      return ReadCode::READ_DONE;
-    }
-
-    ReadCallback* callback = readCallback_;
-    readCallback_ = nullptr;
-    callback->readEOF();
+    recvEOF();
     return ReadCode::READ_DONE;
   }
 }
@@ -3453,53 +3562,7 @@
 
       if (writeReqHead_ == nullptr) {
         writeReqTail_ = nullptr;
-        // This is the last write request.
-        // Unregister for write events and cancel the send timer
-        // before we invoke the callback.  We have to update the state
-        // properly before calling the callback, since it may want to detach
-        // us from the EventBase.
-        if (eventFlags_ & EventHandler::WRITE) {
-          if (!updateEventRegistration(0, EventHandler::WRITE)) {
-            assert(state_ == StateEnum::ERROR);
-            return;
-          }
-          // Stop the send timeout
-          writeTimeout_.cancelTimeout();
-        }
-        assert(!writeTimeout_.isScheduled());
-
-        // If SHUT_WRITE_PENDING is set, we should shutdown the socket after
-        // we finish sending the last write request.
-        //
-        // We have to do this before invoking writeSuccess(), since
-        // writeSuccess() may detach us from our EventBase.
-        if (shutdownFlags_ & SHUT_WRITE_PENDING) {
-          assert(connectCallback_ == nullptr);
-          shutdownFlags_ |= SHUT_WRITE;
-
-          if (shutdownFlags_ & SHUT_READ) {
-            // Reads have already been shutdown.  Fully close the socket and
-            // move to STATE_CLOSED.
-            //
-            // Note: This code currently moves us to STATE_CLOSED even if
-            // close() hasn't ever been called.  This can occur if we have
-            // received EOF from the peer and shutdownWrite() has been called
-            // locally.  Should we bother staying in STATE_ESTABLISHED in this
-            // case, until close() is actually called?  I can't think of a
-            // reason why we would need to do so.  No other operations besides
-            // calling close() or destroying the socket can be performed at
-            // this point.
-            assert(readCallback_ == nullptr);
-            state_ = StateEnum::CLOSED;
-            if (fd_ != NetworkSocket()) {
-              ioHandler_.changeHandlerFD(NetworkSocket());
-              doClose();
-            }
-          } else {
-            // Reads are still enabled, so we are only doing a half-shutdown
-            netops_->shutdown(fd_, SHUT_WR);
-          }
-        }
+        sendDone();
       }
 
       // Invoke the callback
@@ -3512,35 +3575,10 @@
     } else {
       // Partial write.
       writeReqHead_->consume();
-      if (bufferCallback_) {
-        bufferCallback_->onEgressBuffered();
-      }
-      // Stop after a partial write; it's highly likely that a subsequent
-      // write attempt will just return EAGAIN.
-      //
-      // Ensure that we are registered for write events.
-      if ((eventFlags_ & EventHandler::WRITE) == 0) {
-        if (!updateEventRegistration(EventHandler::WRITE, 0)) {
-          assert(state_ == StateEnum::ERROR);
-          return;
-        }
-      }
-
-      // Reschedule the send timeout, since we have made some write progress.
-      if (sendTimeout_ > 0) {
-        if (!writeTimeout_.scheduleTimeout(sendTimeout_)) {
-          AsyncSocketException ex(
-              AsyncSocketException::INTERNAL_ERROR,
-              withAddr("failed to reschedule write timeout"));
-          return failWrite(__func__, ex);
-        }
-      }
+      sendPartial();
       return;
     }
   }
-  if (!writeReqHead_ && bufferCallback_) {
-    bufferCallback_->onEgressBufferCleared();
-  }
 }
 
 void AsyncSocket::checkForImmediateRead() noexcept {
@@ -3560,9 +3598,28 @@
   //
   // The exception to this is if we have pre-received data. In that case there
   // is definitely data available immediately.
+  EventBase* originalEventBase = eventBase_;
   if (preReceivedData_ && !preReceivedData_->empty()) {
-    handleRead();
+    // Even with io_uring requiring ReadCallbacks to take IOBufs via
+    // readBufferAvailable, if the ReadCallback is a small (peeking) read, go
+    // through the normal handleRead() path with
+    // getReadBuffer()/readBufferAvailable().
+    if (iouRecvHandle_ &&
+        readCallback_->maxBufferSize() >= IoUringRecvHandle::kSmallRecvSize) {
+      readCallback_->readBufferAvailable(std::move(preReceivedData_));
+    } else {
+      handleRead();
+    }
   }
+
+  if (iouRecvHandle_ && readCallback_ && eventBase_ == originalEventBase &&
+      iouRecvHandle_->hasQueuedData()) {
+    readCallback_->readBufferAvailable(iouRecvHandle_->getQueuedData());
+  }
+
+  if (iouRecvHandle_ && readCallback_ && eventBase_ == originalEventBase) {
+    iouRecvHandle_->submit(readCallback_->maxBufferSize());
+  }
 }
 
 void AsyncSocket::handleInitialReadWrite() noexcept {
@@ -3572,10 +3629,14 @@
   DestructorGuard dg(this);
   // If we have a readCallback_, make sure we enable read events.  We
   // may already be registered for reads if connectSuccess() set
-  // the read calback.
+  // the read callback.
   if (readCallback_ && !(eventFlags_ & EventHandler::READ)) {
     assert(state_ == StateEnum::ESTABLISHED);
     assert((shutdownFlags_ & SHUT_READ) == 0);
+    if (useIoUring_ && !iouRecvHandle_) {
+      iouRecvHandle_ = IoUringRecvHandle::create(eventBase_, fd_, addr_, this);
+      CHECK(iouRecvHandle_);
+    }
     if (!updateEventRegistration(EventHandler::READ, 0)) {
       assert(state_ == StateEnum::ERROR);
       return;
@@ -3596,12 +3657,22 @@
   if (writeReqHead_ && !(eventFlags_ & EventHandler::WRITE)) {
     // Call handleWrite() to perform write processing.
     handleWrite();
+  } else if (iouSendHandle_ && !iouSendHandle_->empty()) {
+    updateEventRegistration(EventHandler::WRITE, 0);
   } else if (writeReqHead_ == nullptr) {
     // Unregister for write event.
     updateEventRegistration(0, EventHandler::WRITE);
   }
 }
 
+bool AsyncSocket::hasPendingWrites() noexcept {
+  if (useIoUring_) {
+    return iouSendHandle_ && !iouSendHandle_->empty();
+  }
+
+  return writeReqHead_ != nullptr;
+}
+
 void AsyncSocket::handleConnect() noexcept {
   VLOG(5) << "AsyncSocket::handleConnect() this=" << this << ", fd=" << fd_
           << ", state=" << state_;
@@ -3611,13 +3682,15 @@
   // finishes
   assert((shutdownFlags_ & SHUT_WRITE) == 0);
 
-  // In case we had a connect timeout, cancel the timeout
-  writeTimeout_.cancelTimeout();
-  // We don't use a persistent registration when waiting on a connect event,
-  // so we have been automatically unregistered now.  Update eventFlags_ to
-  // reflect reality.
-  assert(eventFlags_ == EventHandler::WRITE);
-  eventFlags_ = EventHandler::NONE;
+  if (!useIoUring_) {
+    // In case we had a connect timeout, cancel the timeout
+    writeTimeout_.cancelTimeout();
+    // We don't use a persistent registration when waiting on a connect event,
+    // so we have been automatically unregistered now.  Update eventFlags_ to
+    // reflect reality.
+    assert(eventFlags_ == EventHandler::WRITE);
+    eventFlags_ = EventHandler::NONE;
+  }
 
   // Call getsockopt() to check if the connect succeeded
   int error;
@@ -3629,29 +3702,33 @@
         AsyncSocketException::INTERNAL_ERROR,
         withAddr("error calling getsockopt() after connect"),
         errnoCopy);
-    if (std::getenv("DISABLE_ASYNC_SOCKET_PRINT") == nullptr) {
-      VLOG(4) << "AsyncSocket::handleConnect(this=" << this << ", fd=" << fd_
-              << " host=" << addr_.describe() << ") exception:" << ex.what();
-    }
+    VLOG(4) << "AsyncSocket::handleConnect(this=" << this << ", fd=" << fd_
+            << " host=" << addr_.describe() << ") exception:" << ex.what();
     return failConnect(__func__, ex);
   }
 
   if (error != 0) {
     AsyncSocketException ex(
         AsyncSocketException::NOT_OPEN, "connect failed", error);
-    if (std::getenv("DISABLE_ASYNC_SOCKET_PRINT") == nullptr) {
-      VLOG(2) << "AsyncSocket::handleConnect(this=" << this << ", fd=" << fd_
-              << " host=" << addr_.describe() << ") exception: " << ex.what();
-    }
+    VLOG(2) << "AsyncSocket::handleConnect(this=" << this << ", fd=" << fd_
+            << " host=" << addr_.describe() << ") exception: " << ex.what();
     return failConnect(__func__, ex);
   }
 
   // Move into STATE_ESTABLISHED
   state_ = StateEnum::ESTABLISHED;
+  if (useIoUring_ && netops_->set_socket_blocking(fd_)) {
+    auto errnoCopy = errno;
+    AsyncSocketException tex(
+        AsyncSocketException::INTERNAL_ERROR,
+        withAddr("failed to restore socket in blocking mode"),
+        errnoCopy);
+    return failConnect(__func__, tex);
+  }
 
   // If SHUT_WRITE_PENDING is set and we don't have any write requests to
   // perform, immediately shutdown the write half of the socket.
-  if ((shutdownFlags_ & SHUT_WRITE_PENDING) && writeReqHead_ == nullptr) {
+  if ((shutdownFlags_ & SHUT_WRITE_PENDING) && !hasPendingWrites()) {
     // SHUT_READ shouldn't be set.  If close() is called on the socket while
     // we are still connecting we just abort the connect rather than waiting
     // for it to complete.
@@ -3929,7 +4006,16 @@
     if (totalWritten >= 0) {
       tfoInfo_.finished = true;
       state_ = StateEnum::ESTABLISHED;
-      // We schedule this asynchrously so that we don't end up
+      if (useIoUring_ && netops_->set_socket_blocking(fd_)) {
+        auto errnoCopy = errno;
+        AsyncSocketException ex(
+            AsyncSocketException::INTERNAL_ERROR,
+            withAddr("failed to restore socket in blocking mode"),
+            errnoCopy);
+        return WriteResult(
+            WRITE_ERROR, std::make_unique<AsyncSocketException>(ex));
+      }
+      // We schedule this asynchronously so that we don't end up
       // invoking initial read or write while a write is in progress.
       scheduleInitialReadWrite();
     } else if (errno == EINPROGRESS) {
@@ -3939,8 +4025,13 @@
       // cookie.
       state_ = StateEnum::CONNECTING;
       try {
-        scheduleConnectTimeout();
-        registerForConnectEvents();
+        if (!useIoUring_) {
+          scheduleConnectTimeout();
+          registerForConnectEvents();
+        } else {
+          iouConnectHandle_ = IoUringConnectHandle::create(
+              eventBase_, fd_, this, connectTimeout_);
+        }
       } catch (const AsyncSocketException& ex) {
         return WriteResult(
             WRITE_ERROR, std::make_unique<AsyncSocketException>(ex));
@@ -3958,6 +4049,15 @@
           // connect succeeded immediately
           // Treat this like no data was written.
           state_ = StateEnum::ESTABLISHED;
+          if (useIoUring_ && netops_->set_socket_blocking(fd_)) {
+            auto errnoCopy = errno;
+            AsyncSocketException ex(
+                AsyncSocketException::INTERNAL_ERROR,
+                withAddr("failed to restore socket in blocking mode"),
+                errnoCopy);
+            return WriteResult(
+                WRITE_ERROR, std::make_unique<AsyncSocketException>(ex));
+          }
           scheduleInitialReadWrite();
         }
         // If there was no exception during connections,
@@ -4050,6 +4150,16 @@
   VLOG(5) << "AsyncSocket::updateEventRegistration(this=" << this
           << ", fd=" << fd_ << ", evb=" << eventBase_ << ", state=" << state_
           << ", events=" << std::hex << eventFlags_;
+  if (useIoUring_) {
+    if (iouSendHandle_) {
+      iouSendHandle_->update(eventFlags_);
+    }
+    if (iouRecvHandle_) {
+      iouRecvHandle_->update(eventFlags_);
+    }
+    return true;
+  }
+
   if (eventFlags_ == EventHandler::NONE) {
     if (ioHandler_.isHandlerRegistered()) {
       DCHECK(eventBase_ != nullptr);
@@ -4103,6 +4213,12 @@
 
   if (eventFlags_ != EventHandler::NONE) {
     eventFlags_ = EventHandler::NONE;
+    if (iouSendHandle_) {
+      iouSendHandle_->update(eventFlags_);
+    }
+    if (iouRecvHandle_) {
+      iouRecvHandle_->update(eventFlags_);
+    }
     ioHandler_.unregisterHandler();
   }
   writeTimeout_.cancelTimeout();
@@ -4199,6 +4315,11 @@
           << "): failed while writing in " << fn << "(): " << ex.what();
   startFail();
 
+  if (iouSendHandle_) {
+    assert(writeReqHead_ == nullptr);
+    iouSendHandle_->failWrite(ex);
+  }
+
   // Only invoke the first write callback, since the error occurred while
   // writing this request.  Let any other pending write callbacks be invoked
   // in finishFail().
@@ -4240,6 +4361,11 @@
 }
 
 void AsyncSocket::failAllWrites(const AsyncSocketException& ex) {
+  if (iouSendHandle_) {
+    assert(writeReqHead_ == nullptr);
+    assert(iouSendHandle_ != nullptr);
+    iouSendHandle_->failAllWrites(ex);
+  }
   // Invoke writeError() on all write callbacks.
   // This is used when writes are forcibly shutdown with write requests
   // pending, or when an error occurs with writes pending.
@@ -4548,6 +4674,170 @@
 
 void AsyncSocket::setBufferCallback(BufferCallback* cb) {
   bufferCallback_ = cb;
+}
+
+void AsyncSocket::connectSuccess() {
+  DestructorGuard dg(this);
+  iouConnectHandle_.reset();
+  handleConnect();
+}
+
+void AsyncSocket::connectTimeout() {
+  DestructorGuard dg(this);
+  if (iouConnectHandle_->cancel()) {
+    iouConnectHandle_.release();
+  } else {
+    iouConnectHandle_.reset();
+  }
+  timeoutExpired();
+}
+
+void AsyncSocket::sendPartial(size_t bytesWritten) {
+  DestructorGuard dg(this);
+  // Only non-zero for io_uring.
+  appBytesWritten_ += bytesWritten;
+  rawBytesWritten_ += bytesWritten;
+
+  if (bufferCallback_) {
+    bufferCallback_->onEgressBuffered();
+  }
+  // Stop after a partial write; it's highly likely that a subsequent
+  // write attempt will just return EAGAIN.
+  //
+  // Ensure that we are registered for write events.
+  if (!useIoUring_ && (eventFlags_ & EventHandler::WRITE) == 0) {
+    if (!updateEventRegistration(EventHandler::WRITE, 0)) {
+      assert(state_ == StateEnum::ERROR);
+      return;
+    }
+  }
+
+  // Reschedule the send timeout, since we have made some write progress.
+  if (sendTimeout_ > 0) {
+    if (!writeTimeout_.scheduleTimeout(sendTimeout_)) {
+      AsyncSocketException ex(
+          AsyncSocketException::INTERNAL_ERROR,
+          withAddr("failed to reschedule write timeout"));
+      return failWrite(__func__, ex);
+    }
+  }
+  return;
+}
+
+void AsyncSocket::sendDone(size_t bytesWritten) {
+  DestructorGuard dg(this);
+  // Only non-zero for io_uring.
+  appBytesWritten_ += bytesWritten;
+  rawBytesWritten_ += bytesWritten;
+
+  // This is the last write request.
+  // Unregister for write events and cancel the send timer
+  // before we invoke the callback.  We have to update the state
+  // properly before calling the callback, since it may want to detach
+  // us from the EventBase.
+  if (eventFlags_ & EventHandler::WRITE) {
+    if (!updateEventRegistration(0, EventHandler::WRITE)) {
+      assert(state_ == StateEnum::ERROR);
+      return;
+    }
+    // Stop the send timeout
+    writeTimeout_.cancelTimeout();
+  }
+  assert(!writeTimeout_.isScheduled());
+
+  // If SHUT_WRITE_PENDING is set, we should shutdown the socket after
+  // we finish sending the last write request.
+  //
+  // We have to do this before invoking writeSuccess(), since
+  // writeSuccess() may detach us from our EventBase.
+  if (shutdownFlags_ & SHUT_WRITE_PENDING) {
+    assert(connectCallback_ == nullptr);
+    shutdownFlags_ |= SHUT_WRITE;
+
+    if (shutdownFlags_ & SHUT_READ) {
+      // Reads have already been shutdown.  Fully close the socket and
+      // move to STATE_CLOSED.
+      //
+      // Note: This code currently moves us to STATE_CLOSED even if
+      // close() hasn't ever been called.  This can occur if we have
+      // received EOF from the peer and shutdownWrite() has been called
+      // locally.  Should we bother staying in STATE_ESTABLISHED in this
+      // case, until close() is actually called?  I can't think of a
+      // reason why we would need to do so.  No other operations besides
+      // calling close() or destroying the socket can be performed at
+      // this point.
+      assert(readCallback_ == nullptr);
+      state_ = StateEnum::CLOSED;
+      if (fd_ != NetworkSocket()) {
+        ioHandler_.changeHandlerFD(NetworkSocket());
+        doClose();
+      }
+    } else {
+      // Reads are still enabled, so we are only doing a half-shutdown
+      netops_->shutdown(fd_, SHUT_WR);
+    }
+  }
+
+  if (!hasPendingWrites() && bufferCallback_) {
+    bufferCallback_->onEgressBufferCleared();
+  }
+}
+
+void AsyncSocket::sendErr(int err) {
+  DestructorGuard dg(this);
+  AsyncSocketException ex(
+      AsyncSocketException::INTERNAL_ERROR,
+      withAddr("async sendmsg() failed"),
+      err);
+  failWrite(__func__, ex);
+}
+
+void AsyncSocket::recvSuccess(std::unique_ptr<IOBuf> buf) {
+  DestructorGuard dg(this);
+  DCHECK(readAncillaryDataCallback_ == nullptr);
+  CHECK(readCallback_);
+
+  if (preReceivedData_ && !preReceivedData_->empty()) {
+    preReceivedData_->appendToChain(std::move(buf));
+    auto bytes = preReceivedData_->computeChainDataLength();
+    readCallback_->readBufferAvailable(std::move(preReceivedData_));
+    appBytesReceived_ += bytes;
+    return;
+  }
+
+  auto bytes = buf->computeChainDataLength();
+  readCallback_->readBufferAvailable(std::move(buf));
+  appBytesReceived_ += bytes;
+}
+
+void AsyncSocket::recvEOF() noexcept {
+  DestructorGuard dg(this);
+  readErr_ = READ_EOF;
+  // EOF
+  shutdownFlags_ |= SHUT_READ;
+  if (!updateEventRegistration(0, EventHandler::READ)) {
+    // we've already been moved into STATE_ERROR
+    assert(state_ == StateEnum::ERROR);
+    assert(readCallback_ == nullptr);
+    return;
+  }
+
+  ReadCallback* callback = readCallback_;
+  readCallback_ = nullptr;
+  callback->readEOF();
+}
+
+void AsyncSocket::recvErr(
+    int err, std::unique_ptr<const AsyncSocketException> exception) noexcept {
+  DestructorGuard dg(this);
+  readErr_ = READ_ERROR;
+  if (exception) {
+    failRead(__func__, *exception);
+    return;
+  }
+  AsyncSocketException ex(
+      AsyncSocketException::INTERNAL_ERROR, withAddr("recv() failed"), err);
+  failRead(__func__, ex);
 }
 
 std::ostream& operator<<(
diff --git a/folly/folly/io/async/AsyncSocket.h b/folly/folly/io/async/AsyncSocket.h
--- a/folly/folly/io/async/AsyncSocket.h
+++ b/folly/folly/io/async/AsyncSocket.h
@@ -36,6 +36,9 @@
 #include <folly/io/async/AsyncTransport.h>
 #include <folly/io/async/DelayedDestruction.h>
 #include <folly/io/async/EventHandler.h>
+#include <folly/io/async/IoUringConnect.h>
+#include <folly/io/async/IoUringRecv.h>
+#include <folly/io/async/IoUringSend.h>
 #include <folly/io/async/observer/AsyncSocketObserverContainer.h>
 #include <folly/net/NetOpsDispatcher.h>
 #include <folly/net/TcpInfo.h>
@@ -73,10 +76,22 @@
  * responding and no further progress can be made sending the data.
  */
 
-#if defined __linux__ && !defined SO_NO_TRANSPARENT_TLS
-#define SO_NO_TRANSPARENT_TLS 200
+/**
+ *This is a @deprecated approach to disabling TTLS and should be
+ *removed after completing the migration to FOLLY_SO_TTLS_TRUSTED.
+ */
+#if defined __linux__ && !defined FOLLY_SO_NO_TRANSPARENT_TLS
+#define FOLLY_SO_NO_TRANSPARENT_TLS 200
 #endif
 
+#if defined __linux__ && !defined FOLLY_SO_TTLS_TRUSTED
+#define FOLLY_SO_TTLS_TRUSTED 206
+#endif
+
+#if defined __linux__ && !defined FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED
+#define FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED 1
+#endif
+
 #if defined __linux__ && !defined SO_NO_TSOCKS
 #define SO_NO_TSOCKS 201
 #endif
@@ -85,13 +100,19 @@
 #define SO_MAX_ATTEMPTS_ENABLE_BYTEEVENTS 10
 #endif
 
-class AsyncSocket : public AsyncSocketTransport {
+class AsyncSocket
+    : public AsyncSocketTransport,
+      public IoUringSendCallback,
+      public IoUringRecvCallback,
+      public IoUringConnectCallback {
  public:
   using UniquePtr = std::unique_ptr<AsyncSocket, Destructor>;
   using ByteEvent = AsyncSocketObserverInterface::ByteEvent;
   using Observer = AsyncSocketObserverContainer::Observer;
   using ManagedObserver = AsyncSocketObserverContainer::ManagedObserver;
 
+  static inline constexpr size_t kMaxAttemptsEnableByteEvents = 10;
+
   class EvbChangeCallback {
    public:
     virtual ~EvbChangeCallback() = default;
@@ -395,7 +416,7 @@
      *         Otherwise, returns an empty optional.
      *
      *         If the helper has previously thrown a ByteEventHelper::Exception,
-     *         it will not process further Cmsg and will continiously return an
+     *         it will not process further Cmsg and will continuously return an
      *         empty optional.
      *
      * @throw  If the helper receives a sequence of Cmsg that violate its
@@ -688,7 +709,7 @@
    * receiving notifications for messages posted to the error queue
    * associated with the socket.
    * ErrMessageCallback is implemented only for platforms with
-   * per-socket error message queus support (recvmsg() system call must
+   * per-socket error message queues support (recvmsg() system call must
    * )
    *
    */
@@ -781,13 +802,6 @@
   // Read and write methods
   void setReadCB(ReadCallback* callback) override;
   ReadCallback* getReadCallback() const override;
-  void setEventCallback(EventRecvmsgCallback* cb) override {
-    if (cb) {
-      ioHandler_.setEventCallback(cb);
-    } else {
-      ioHandler_.resetEventCallback();
-    }
-  }
 
   /**
    * Create a memory store to use for zero copy reads.
@@ -926,7 +940,7 @@
   }
 
   /**
-   * Returns when connect() finished (either successsfully or failed).
+   * Returns when connect() finished (either successfully or failed).
    */
   std::chrono::steady_clock::time_point getConnectEndTime() const {
     return connectEndTime_;
@@ -958,7 +972,7 @@
   bool getTFOFinished() const { return tfoInfo_.finished; }
 
   /**
-   * Returns whether or not TFO attempt succeded on this
+   * Returns whether or not TFO attempt succeeded on this
    * connection.
    * For servers this is pretty straightforward API and can
    * be invoked right after the connection is accepted. This API
@@ -969,7 +983,7 @@
    * data is read from the socket when the caller knows that
    * the SYN has been ACKed by the server.
    */
-  bool getTFOSucceded() const override;
+  bool getTFOSucceeded() const override;
 
   // Methods controlling socket options
 
@@ -1148,6 +1162,12 @@
    */
   void setTosOrTrafficClass(int tosOrTrafficClass);
 
+  /**
+   * This flag controls whether or not IP_BIND_ADDRESS_NO_PORT is enabled for
+   * AsyncSocket sockets. This is enabled by default.
+   */
+  void setBindAddressNoPort(bool flag) { bindAddressNoPort_ = flag; }
+
   void disableTransparentTls() override { noTransparentTls_ = true; }
 
   void disableTSocks() { noTSocks_ = true; }
@@ -1375,7 +1395,7 @@
     /**
      * Constructor for observer.
      *
-     * @param config      Config, defaults to auxilary instrumentaton disabled.
+     * @param config      Config, defaults to auxilary instrumentation disabled.
      */
     explicit LegacyLifecycleObserver(const Config& observerConfig)
         : observerConfig_(observerConfig) {}
@@ -1449,7 +1469,7 @@
    *
    * @return             Vector with installed observers.
    */
-  FOLLY_NODISCARD virtual std::vector<LegacyLifecycleObserver*>
+  [[nodiscard]] virtual std::vector<LegacyLifecycleObserver*>
   getLifecycleObservers() const;
 
   /**
@@ -1683,6 +1703,7 @@
   virtual void handleWrite() noexcept;
   virtual void handleConnect() noexcept;
   void timeoutExpired() noexcept;
+  bool hasPendingWrites() noexcept;
 
   /**
    * Handler for when the file descriptor is attached to the AsyncSocket.
@@ -1871,8 +1892,9 @@
 
   void drainZeroCopyQueue();
 
-  virtual void releaseIOBuf(
-      std::unique_ptr<folly::IOBuf> buf, ReleaseIOBufCallback* callback);
+  void releaseIOBuf(
+      std::unique_ptr<folly::IOBuf> buf,
+      ReleaseIOBufCallback* callback) override;
 
   ReadCode processZeroCopyRead();
   ReadCode processNormalRead();
@@ -1897,6 +1919,28 @@
    */
   virtual void enableByteEvents();
 
+  /*
+   * IoUringConnectCallback
+   */
+  void connectSuccess() override;
+  void connectTimeout() override;
+
+  /*
+   * IoUringSendCallback
+   */
+  void sendPartial(size_t bytesWritten = 0) override;
+  void sendDone(size_t bytesWritten = 0) override;
+  void sendErr(int err) override;
+
+  /*
+   * IoUringRecvCallback
+   */
+  void recvSuccess(std::unique_ptr<IOBuf> data) override;
+  void recvEOF() noexcept override;
+  void recvErr(
+      int err,
+      std::unique_ptr<const AsyncSocketException> exception) noexcept override;
+
   AsyncWriter::ZeroCopyEnableFunc zeroCopyEnableFunc_;
 
   // a folly::IOBuf can be used in multiple partial requests
@@ -1996,6 +2040,7 @@
   };
 
   TCPFastOpenInfo tfoInfo_;
+  bool bindAddressNoPort_{true};
   bool noTransparentTls_{false};
   bool noTSocks_{false};
   // Whether to track EOR or not.
@@ -2016,6 +2061,11 @@
   int zerocopyReadErr_{0};
 
   bool closeOnFailedWrite_{true};
+
+  bool useIoUring_{false};
+  IoUringConnectHandle::UniquePtr iouConnectHandle_;
+  IoUringSendHandle::UniquePtr iouSendHandle_;
+  IoUringRecvHandle::UniquePtr iouRecvHandle_;
 
   netops::DispatcherContainer netops_;
 
diff --git a/folly/folly/io/async/AsyncSocketException.h b/folly/folly/io/async/AsyncSocketException.h
--- a/folly/folly/io/async/AsyncSocketException.h
+++ b/folly/folly/io/async/AsyncSocketException.h
@@ -46,13 +46,18 @@
     CANCELED = 17,
   };
 
+  /// Asserts (in debug builds) the errno value to be nonnegative to help with
+  /// linux io-uring, which passes errno values as negative numbers. But permits
+  /// a special negative number which does not map to any actual errno errors.
   AsyncSocketException(
       AsyncSocketExceptionType type,
       const std::string& message,
       int errnoCopy = 0)
       : std::runtime_error(getMessage(type, message, errnoCopy)),
         type_(type),
-        errno_(errnoCopy) {}
+        errno_(errnoCopy) {
+    assert(errnoCopy >= 0 || errnoCopy == INT_MIN);
+  }
 
   AsyncSocketExceptionType getType() const noexcept { return type_; }
 
diff --git a/folly/folly/io/async/AsyncSocketTransport.cpp b/folly/folly/io/async/AsyncSocketTransport.cpp
--- a/folly/folly/io/async/AsyncSocketTransport.cpp
+++ b/folly/folly/io/async/AsyncSocketTransport.cpp
@@ -24,4 +24,16 @@
   return anyAddress;
 }
 
+int AsyncSocketTransport::getNapiId() const {
+#if defined(__linux__)
+  int id = -1;
+  socklen_t len = sizeof(id);
+  auto ret = ::getsockopt(
+      getNetworkSocket().toFd(), SOL_SOCKET, SO_INCOMING_NAPI_ID, &id, &len);
+  return ret < 0 || id < 1 ? -1 : id;
+#else
+  return -1;
+#endif
+}
+
 } // namespace folly
diff --git a/folly/folly/io/async/AsyncSocketTransport.h b/folly/folly/io/async/AsyncSocketTransport.h
--- a/folly/folly/io/async/AsyncSocketTransport.h
+++ b/folly/folly/io/async/AsyncSocketTransport.h
@@ -95,8 +95,10 @@
     return selfCertData_.get();
   }
 
+  int getNapiId() const override;
+
   virtual NetworkSocket getNetworkSocket() const = 0;
-  virtual bool getTFOSucceded() const = 0;
+  virtual bool getTFOSucceeded() const = 0;
   virtual void enableTFO() = 0;
   virtual void disableTransparentTls() {}
 
diff --git a/folly/folly/io/async/AsyncTimeout.h b/folly/folly/io/async/AsyncTimeout.h
--- a/folly/folly/io/async/AsyncTimeout.h
+++ b/folly/folly/io/async/AsyncTimeout.h
@@ -33,7 +33,7 @@
  */
 class AsyncTimeout {
  public:
-  typedef TimeoutManager::InternalEnum InternalEnum;
+  using InternalEnum = TimeoutManager::InternalEnum;
 
   /**
    * Create a new AsyncTimeout object, driven by the specified TimeoutManager.
diff --git a/folly/folly/io/async/AsyncTransport.h b/folly/folly/io/async/AsyncTransport.h
--- a/folly/folly/io/async/AsyncTransport.h
+++ b/folly/folly/io/async/AsyncTransport.h
@@ -277,7 +277,6 @@
   // Read methods that aren't part of AsyncTransport.
   virtual void setReadCB(ReadCallback* callback) = 0;
   virtual ReadCallback* getReadCallback() const = 0;
-  virtual void setEventCallback(EventRecvmsgCallback* /*cb*/) {}
   virtual std::unique_ptr<IOBuf> takePreReceivedData() { return {}; }
 
  protected:
@@ -392,11 +391,11 @@
     size_t mapSize{0};
   };
 
-  FOLLY_NODISCARD virtual bool setRXZeroCopy(RXZerocopyParams /*params*/) {
+  [[nodiscard]] virtual bool setRXZeroCopy(RXZerocopyParams /*params*/) {
     return false;
   }
 
-  FOLLY_NODISCARD virtual bool getRXZeroCopy() const { return false; }
+  [[nodiscard]] virtual bool getRXZeroCopy() const { return false; }
 
   using ZeroCopyEnableFunc =
       std::function<bool(const std::unique_ptr<folly::IOBuf>& buf)>;
@@ -440,7 +439,7 @@
       public AsyncReader,
       public AsyncWriter {
  public:
-  typedef std::unique_ptr<AsyncTransport, Destructor> UniquePtr;
+  using UniquePtr = std::unique_ptr<AsyncTransport, Destructor>;
 
   /**
    * Close the transport.
@@ -796,6 +795,16 @@
       CHECK(false) << "setReplaySafetyCallback() not supported";
     }
   }
+
+  /**
+   * Return SO_INCOMING_NAPI_ID for this transport. For socket transports, this
+   * is associated with the NAPI instance/receive queue. For other transports,
+   * it is not defined.
+   *
+   * Returns -1 for error or invalid NAPI ID, or a positive integer for a valid
+   * NAPI ID.
+   */
+  virtual int getNapiId() const { return -1; }
 
  public:
   /**
diff --git a/folly/folly/io/async/AsyncUDPServerSocket.h b/folly/folly/io/async/AsyncUDPServerSocket.h
--- a/folly/folly/io/async/AsyncUDPServerSocket.h
+++ b/folly/folly/io/async/AsyncUDPServerSocket.h
@@ -136,8 +136,6 @@
         validateSocketOptions(
             options, addy.getFamily(), SocketOptionKey::ApplyPos::POST_BIND),
         SocketOptionKey::ApplyPos::POST_BIND);
-
-    applyEventCallback();
   }
 
   void setReusePort(bool reusePort) { reusePort_ = reusePort; }
@@ -255,16 +253,6 @@
     }
   }
 
-  void setEventCallback(EventRecvmsgCallback* cb) {
-    eventCb_ = cb;
-    applyEventCallback();
-  }
-
-  void setRecvmsgMultishotCallback(EventRecvmsgMultishotCallback* cb) {
-    multishotCb_ = cb;
-    applyEventCallback();
-  }
-
   bool setTimestamping(int val) { return socket_->setTimestamping(val); }
 
  private:
@@ -344,25 +332,13 @@
     }
   }
 
-  void applyEventCallback() {
-    if (socket_) {
-      if (eventCb_) {
-        socket_->setEventCallback(eventCb_);
-      } else if (multishotCb_) {
-        socket_->setRecvmsgMultishotCallback(multishotCb_);
-      } else {
-        socket_->resetEventCallback();
-      }
-    }
-  }
-
   EventBase* const evb_;
   const size_t packetSize_;
 
   std::shared_ptr<AsyncUDPSocket> socket_;
 
   // List of listener to distribute packets among
-  typedef std::pair<EventBase*, Callback*> Listener;
+  using Listener = std::pair<EventBase*, Callback*>;
   std::vector<Listener> listeners_;
 
   DispatchMechanism dispatchMechanism_;
@@ -376,9 +352,6 @@
   bool reusePort_{false};
   bool reuseAddr_{false};
   bool recvTos_{false};
-
-  EventRecvmsgCallback* eventCb_{nullptr};
-  EventRecvmsgMultishotCallback* multishotCb_{nullptr};
 };
 
 } // namespace folly
diff --git a/folly/folly/io/async/CertificateIdentityVerifier.h b/folly/folly/io/async/CertificateIdentityVerifier.h
--- a/folly/folly/io/async/CertificateIdentityVerifier.h
+++ b/folly/folly/io/async/CertificateIdentityVerifier.h
@@ -16,19 +16,28 @@
 
 #pragma once
 
-#include <folly/Try.h>
-#include <folly/Unit.h>
+#include <memory>
+
 #include <folly/io/async/AsyncTransportCertificate.h>
+#include <folly/portability/OpenSSL.h>
 
 namespace folly {
 
 /**
  * CertificateIdentityVerifier implementations are used during TLS handshakes to
- * extract and verify end-entity certificate identities. AsyncSSLSocket first
- * performs OpenSSL certificate chain validation and then invokes any registered
- * HandshakeCB's handshakeVer() method. Only if both of these succeed, it then
- * calls the verifyLeaf method to further verify a peer's certificate.
+ * extract and verify end-entity certificate identities.
  *
+ * During TLS handshake, AsyncSSLSocket performs verification in this order:
+ * 1. OpenSSL performs internal certificate chain validation
+ * 2. HandshakeCB's handshakeVer() is invoked (if registered)
+ * 3. CertificateIdentityVerifier's verifyContext() is invoked (if registered)
+ *    - Called once for EACH certificate in the chain (root → intermediate →
+ * leaf)
+ *    - Provides access to the full X509_STORE_CTX for chain inspection
+ * 4. CertificateIdentityVerifier's verifyLeaf() is invoked (if registered)
+ *    - Called ONLY for the leaf certificate at depth 0
+ *    - Only if all previous verification steps succeeded
+ *
  * TLS connections must pass all these checks in order for an AsyncSSLSocket's
  * registered HandshakeCB to receive handshakeSuc().
  *
@@ -38,6 +47,50 @@
 class CertificateIdentityVerifier {
  public:
   virtual ~CertificateIdentityVerifier() = default;
+
+  /**
+   * AsyncSSLSocket calls verifyContext() during the OpenSSL certificate
+   * verification callback for EACH certificate in the chain (typically
+   * root → intermediate(s) → leaf).
+   *
+   * This method allows custom validation logic beyond OpenSSL's internal
+   * consistency checks. The returned value becomes the new verification state:
+   * - If this method returns false, verification fails and verifyLeaf() will
+   *   NOT be called
+   * - If this method returns true, verification succeeds (and verifyLeaf()
+   *   may be called for the leaf certificate at depth 0)
+   *
+   * This method can override OpenSSL's verification result. For example:
+   * - If OpenSSL fails (preverifyOk=false) but verifyContext returns true,
+   *   verification succeeds and verifyLeaf() may be called
+   * - If OpenSSL succeeds (preverifyOk=true) but verifyContext returns false,
+   *   verification fails and verifyLeaf() will NOT be called
+   *
+   * Unlike HandshakeCB::handshakeVer(), this method does not short-circuit
+   * when it changes the result - it updates the verification state and allows
+   * processing to continue.
+   *
+   * The default implementation returns preverifyOk unchanged (pass-through),
+   * deferring to OpenSSL's verification decision.
+   *
+   * IMPORTANT: This method is called multiple times per handshake - once for
+   * each certificate in the chain. Use X509_STORE_CTX_get_error_depth(ctx)
+   * to determine the depth of the certificate being verified.
+   *
+   * See the passages on verify_callback in SSL_CTX_set_verify(3) for more
+   * details.
+   *
+   * @param preverifyOk Result of OpenSSL's internal verification checks
+   * @param ctx OpenSSL verification context containing the certificate chain
+   *            and verification state. Use
+   * X509_STORE_CTX_get_current_cert(&ctx) to access the certificate being
+   * verified at this depth.
+   * @return true if the certificate context is valid, false otherwise
+   */
+  virtual bool verifyContext(
+      bool preverifyOk, [[maybe_unused]] X509_STORE_CTX* ctx) const noexcept {
+    return preverifyOk;
+  }
 
   /**
    * AsyncSSLSocket calls verifyLeaf() during a TLS handshake after chain
diff --git a/folly/folly/io/async/DelayedDestructionBase.h b/folly/folly/io/async/DelayedDestructionBase.h
--- a/folly/folly/io/async/DelayedDestructionBase.h
+++ b/folly/folly/io/async/DelayedDestructionBase.h
@@ -62,7 +62,7 @@
    * object, causing problems when the callback function returns and the
    * guarded object's method resumes execution.
    */
-  class FOLLY_NODISCARD DestructorGuard {
+  class [[nodiscard]] DestructorGuard {
    public:
     explicit DestructorGuard(DelayedDestructionBase* dd) : dd_(dd) {
       if (dd_ != nullptr) {
diff --git a/folly/folly/io/async/EpollBackend.cpp b/folly/folly/io/async/EpollBackend.cpp
--- a/folly/folly/io/async/EpollBackend.cpp
+++ b/folly/folly/io/async/EpollBackend.cpp
@@ -53,7 +53,7 @@
 
 struct SignalRegistry {
   struct SigInfo {
-    struct sigaction sa_ {};
+    struct sigaction sa_{};
     size_t refs_{0};
   };
   using SignalMap = std::map<int, SigInfo>;
diff --git a/folly/folly/io/async/EventBase.cpp b/folly/folly/io/async/EventBase.cpp
--- a/folly/folly/io/async/EventBase.cpp
+++ b/folly/folly/io/async/EventBase.cpp
@@ -305,6 +305,8 @@
 }
 
 EventBase::~EventBase() {
+  DCheckRequestContextRestoredGuard dcheckRctxGuard;
+
   // Relax strict mode to allow callbacks to run in the destructor outside of
   // the main loop. Note that any methods (including driving the loop) must be
   // called before the destructor starts, so it is safe to modify the variable.
@@ -318,6 +320,7 @@
     while (!callbacks.empty()) {
       auto& callback = callbacks.front();
       callbacks.pop_front();
+      RequestContextSaverScopeGuard rctxGuard;
       callback.runCallback();
     }
   }
@@ -346,6 +349,7 @@
     while (!callbacks.empty()) {
       auto& callback = callbacks.front();
       callbacks.pop_front();
+      RequestContextSaverScopeGuard rctxGuard;
       callback.runCallback();
     }
   }
@@ -539,10 +543,11 @@
   loopMainSetup();
 }
 
-bool EventBase::loopPoll() {
+bool EventBase::loopPoll(LoopPollOptions options) {
   DCHECK(isRunning());
   dcheckIsInEventBaseThread();
-  return isSuccess(loopMain(EVLOOP_NONBLOCK | EVLOOP_ONCE, {}));
+  return isSuccess(
+      loopMain((options.nonblock ? EVLOOP_NONBLOCK : 0) | EVLOOP_ONCE, {}));
 }
 
 void EventBase::loopPollCleanup() {
@@ -583,6 +588,8 @@
 }
 
 EventBase::LoopStatus EventBase::loopMain(int flags, LoopOptions options) {
+  DCheckRequestContextRestoredGuard dcheckRctxGuard;
+
   int res = 0;
   bool blocking = !(flags & EVLOOP_NONBLOCK);
   bool once = (flags & EVLOOP_ONCE);
@@ -668,6 +675,7 @@
       maxLatencyLoopTime_.addSample(loop_time, busy);
 
       if (observer_) {
+        RequestContextSaverScopeGuard rctxGuard;
         if (++observerSampleCount_ >= observer_->getSampleRate()) {
           observerSampleCount_ = 0;
           observer_->loopSample(busy.count(), idle.count());
@@ -945,6 +953,21 @@
   queue_->putMessage(std::move(fn));
 }
 
+void EventBase::runInEventBaseThreadAlwaysEnqueue(
+    std::shared_ptr<RequestContext>&& ctx, Func fn) noexcept {
+  // Send the message.
+  // It will be received by the FunctionRunner in the EventBase's thread.
+
+  // We try not to schedule nullptr callbacks
+  if (!fn) {
+    LOG(DFATAL) << "EventBase " << this
+                << ": Scheduling nullptr callbacks is not allowed";
+    return;
+  }
+
+  queue_->putMessage(std::move(ctx), std::move(fn));
+}
+
 void EventBase::runInEventBaseThreadAndWait(Func fn) noexcept {
   if (inRunningEventBaseThread()) {
     LOG(DFATAL) << "EventBase " << this << ": Waiting in the event loop is not "
@@ -966,6 +989,7 @@
 
 void EventBase::runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept {
   if (isInEventBaseThread()) {
+    RequestContextSaverScopeGuard rctxGuard;
     fn();
   } else {
     runInEventBaseThreadAndWait(std::move(fn));
@@ -974,6 +998,7 @@
 
 void EventBase::runImmediatelyOrRunInEventBaseThread(Func fn) noexcept {
   if (isInEventBaseThread()) {
+    RequestContextSaverScopeGuard rctxGuard;
     fn();
   } else {
     runInEventBaseThreadAlwaysEnqueue(std::move(fn));
diff --git a/folly/folly/io/async/EventBase.h b/folly/folly/io/async/EventBase.h
--- a/folly/folly/io/async/EventBase.h
+++ b/folly/folly/io/async/EventBase.h
@@ -170,9 +170,8 @@
     bool isLoopCallbackScheduled() const { return is_linked(); }
 
    private:
-    typedef boost::intrusive::
-        list<LoopCallback, boost::intrusive::constant_time_size<false>>
-            List;
+    using List = boost::intrusive::
+        list<LoopCallback, boost::intrusive::constant_time_size<false>>;
 
     // EventBase needs access to LoopCallbackList (and therefore to hook_)
     friend class EventBase;
@@ -412,6 +411,11 @@
    */
   bool loopOnce(int flags = 0);
 
+  struct LoopPollOptions {
+    bool nonblock = true;
+    LoopPollOptions() {}
+  };
+
   /**
    * Poll the EventBase for active events, run them, then return. Unlike
    * loopOnce, the expectation is that loopPoll will be called multiple times
@@ -428,8 +432,13 @@
    *
    * Must be called within a corresponding pair of loopPollSetup and
    * loopPollCleanup; may be called many times within the pair.
+   *
+   * @param option: whether the function can block waiting for active events
+   *  true:  return immediately after all active events are processed.
+   *  false: block and keep running active events, until waken up by
+   * notification.
    */
-  bool loopPoll();
+  bool loopPoll(LoopPollOptions options = {});
 
   /**
    * Sets up state for active polling to be done against the EventBase. Call
@@ -640,26 +649,7 @@
    *
    * @param fn  The function to run.  The function must not throw any
    *     exceptions.
-   * @param arg An argument to pass to the function.
    */
-  template <typename T>
-  void runInEventBaseThread(void (*fn)(T*), T* arg) noexcept;
-
-  /**
-   * Run the specified function in the EventBase's thread
-   *
-   * This version of runInEventBaseThread() takes a folly::Function object.
-   * Note that this may be less efficient than the version that takes a plain
-   * function pointer and void* argument, if moving the function is expensive
-   * (e.g., if it wraps a lambda which captures some values with expensive move
-   * constructors).
-   *
-   * If the loop is terminated (and never later restarted) before it has a
-   * chance to run the requested function, the function will be run upon the
-   * EventBase's destruction.
-   *
-   * The function must not throw any exceptions.
-   */
   void runInEventBaseThread(Func fn) noexcept;
 
   /**
@@ -684,66 +674,34 @@
    *
    * @param fn  The function to run.  The function must not throw any
    *     exceptions.
-   * @param arg An argument to pass to the function.
    */
-  template <typename T>
-  void runInEventBaseThreadAlwaysEnqueue(void (*fn)(T*), T* arg) noexcept;
+  void runInEventBaseThreadAlwaysEnqueue(Func fn) noexcept;
 
   /**
    * Run the specified function in the EventBase's thread
    *
-   * This version of runInEventBaseThreadAlwaysEnqueue() takes a folly::Function
-   * object. Note that this may be less efficient than the version that takes a
-   * plain function pointer and void* argument, if moving the function is
-   * expensive (e.g., if it wraps a lambda which captures some values with
-   * expensive move constructors).
-   *
-   * If the loop is terminated (and never later restarted) before it has a
-   * chance to run the requested function, the function will be run upon the
-   * EventBase's destruction.
-   *
-   * The function must not throw any exceptions.
+   * This version takes a folly::Function and a RequestContext which will be in
+   * scope when the Function is invoked.
    */
-  void runInEventBaseThreadAlwaysEnqueue(Func fn) noexcept;
+  void runInEventBaseThreadAlwaysEnqueue(
+      std::shared_ptr<RequestContext>&& ctx, Func fn) noexcept;
 
   /*
    * Like runInEventBaseThread, but the caller waits for the callback to be
    * executed.
    */
-  template <typename T>
-  void runInEventBaseThreadAndWait(void (*fn)(T*), T* arg) noexcept;
-
-  /**
-   * Like runInEventBaseThread, but the caller waits for the callback to be
-   * executed.
-   */
   void runInEventBaseThreadAndWait(Func fn) noexcept;
 
   /**
    * Like runInEventBaseThreadAndWait, except if the caller is already in the
    * event base thread, the functor is simply run inline.
    */
-  template <typename T>
-  void runImmediatelyOrRunInEventBaseThreadAndWait(
-      void (*fn)(T*), T* arg) noexcept;
-
-  /**
-   * Like runInEventBaseThreadAndWait, except if the caller is already in the
-   * event base thread, the functor is simply run inline.
-   */
   void runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept;
 
   /**
    * Like runInEventBaseThread, but runs function immediately instead of at the
    * end of the loop when called from the eventbase thread.
    */
-  template <typename T>
-  void runImmediatelyOrRunInEventBaseThread(void (*fn)(T*), T* arg) noexcept;
-
-  /**
-   * Like runInEventBaseThread, but runs function immediately instead of at the
-   * end of the loop when called from the eventbase thread.
-   */
   void runImmediatelyOrRunInEventBaseThread(Func fn) noexcept;
 
   /**
@@ -1029,7 +987,7 @@
    */
   bool nothingHandledYet() const noexcept;
 
-  typedef LoopCallback::List LoopCallbackList;
+  using LoopCallbackList = LoopCallback::List;
 
   bool isSuccess(LoopStatus status);
 
@@ -1151,33 +1109,5 @@
 
   std::unique_ptr<ThreadIdCollector> threadIdCollector_;
 };
-
-template <typename T>
-void EventBase::runInEventBaseThread(void (*fn)(T*), T* arg) noexcept {
-  return runInEventBaseThread([=] { fn(arg); });
-}
-
-template <typename T>
-void EventBase::runInEventBaseThreadAlwaysEnqueue(
-    void (*fn)(T*), T* arg) noexcept {
-  return runInEventBaseThreadAlwaysEnqueue([=] { fn(arg); });
-}
-
-template <typename T>
-void EventBase::runInEventBaseThreadAndWait(void (*fn)(T*), T* arg) noexcept {
-  return runInEventBaseThreadAndWait([=] { fn(arg); });
-}
-
-template <typename T>
-void EventBase::runImmediatelyOrRunInEventBaseThreadAndWait(
-    void (*fn)(T*), T* arg) noexcept {
-  return runImmediatelyOrRunInEventBaseThreadAndWait([=] { fn(arg); });
-}
-
-template <typename T>
-void EventBase::runImmediatelyOrRunInEventBaseThread(
-    void (*fn)(T*), T* arg) noexcept {
-  return runImmediatelyOrRunInEventBaseThread([=] { fn(arg); });
-}
 
 } // namespace folly
diff --git a/folly/folly/io/async/EventBaseAtomicNotificationQueue.h b/folly/folly/io/async/EventBaseAtomicNotificationQueue.h
--- a/folly/folly/io/async/EventBaseAtomicNotificationQueue.h
+++ b/folly/folly/io/async/EventBaseAtomicNotificationQueue.h
@@ -92,7 +92,7 @@
    * Returns true iff the task was queued.
    * Can be called from any thread.
    */
-  FOLLY_NODISCARD bool tryPutMessage(Task&& task, uint32_t maxSize);
+  [[nodiscard]] bool tryPutMessage(Task&& task, uint32_t maxSize);
 
   /*
    * Detaches the queue from an EventBase.
diff --git a/folly/folly/io/async/EventBaseBackendBase.cpp b/folly/folly/io/async/EventBaseBackendBase.cpp
--- a/folly/folly/io/async/EventBaseBackendBase.cpp
+++ b/folly/folly/io/async/EventBaseBackendBase.cpp
@@ -65,36 +65,4 @@
   return evb_ ? evb_->getBackend() : nullptr;
 }
 
-bool EventRecvmsgMultishotCallback::parseRecvmsgMultishot(
-    ByteRange total, struct msghdr const& msghdr, ParsedRecvMsgMultishot& out) {
-  struct H {
-    uint32_t name;
-    uint32_t control;
-    uint32_t payload;
-    uint32_t flags;
-  };
-  size_t const header = sizeof(H) + msghdr.msg_namelen + msghdr.msg_controllen;
-  if (total.size() < header) {
-    return false;
-  }
-  H const* h = reinterpret_cast<H const*>(total.data());
-  out.realNameLength = h->name;
-  if (static_cast<uint32_t>(msghdr.msg_namelen) >= h->name) {
-    out.name = total.subpiece(sizeof(H), h->name);
-  } else {
-    out.name = total.subpiece(sizeof(H), msghdr.msg_namelen);
-  }
-
-  out.control = total.subpiece(sizeof(H) + msghdr.msg_namelen, h->control);
-  out.payload = total.subpiece(header);
-  out.realPayloadLength = h->payload;
-  out.flags = h->flags;
-
-  if (out.payload.size() != h->payload) {
-    LOG(ERROR) << "odd size " << out.payload.size() << " vs " << h->payload;
-    return false;
-  }
-  return true;
-}
-
 } // namespace folly
diff --git a/folly/folly/io/async/EventBaseBackendBase.h b/folly/folly/io/async/EventBaseBackendBase.h
--- a/folly/folly/io/async/EventBaseBackendBase.h
+++ b/folly/folly/io/async/EventBaseBackendBase.h
@@ -29,106 +29,6 @@
 class EventBase;
 class EventBaseBackendBase;
 
-class EventReadCallback {
- public:
-  struct IoVec {
-    virtual ~IoVec() = default;
-    using FreeFunc = void (*)(IoVec*);
-    using CallbackFunc = void (*)(IoVec*, int);
-    void* arg_{nullptr};
-    struct iovec data_;
-    FreeFunc freeFunc_{nullptr};
-    CallbackFunc cbFunc_{nullptr};
-  };
-
-  EventReadCallback() = default;
-  virtual ~EventReadCallback() = default;
-
-  virtual IoVec* allocateData() noexcept = 0;
-};
-
-class EventRecvmsgCallback {
- public:
-  struct MsgHdr {
-    virtual ~MsgHdr() = default;
-    using FreeFunc = void (*)(MsgHdr*);
-    using CallbackFunc = void (*)(MsgHdr*, int);
-    void* arg_{nullptr};
-    struct msghdr data_;
-    FreeFunc freeFunc_{nullptr};
-    CallbackFunc cbFunc_{nullptr};
-  };
-
-  EventRecvmsgCallback() = default;
-  virtual ~EventRecvmsgCallback() = default;
-
-  virtual MsgHdr* allocateData() noexcept = 0;
-};
-
-class EventRecvmsgMultishotCallback {
- public:
-  struct Hdr {
-    virtual ~Hdr() = default;
-    using FreeFunc = void (*)(Hdr*);
-    using CallbackFunc = void (*)(Hdr*, int, std::unique_ptr<IOBuf> io_buf);
-    void* arg_{nullptr};
-    struct msghdr data_;
-    FreeFunc freeFunc_{nullptr};
-    CallbackFunc cbFunc_{nullptr};
-  };
-
-  struct ParsedRecvMsgMultishot {
-    folly::ByteRange payload;
-    folly::ByteRange name;
-    folly::ByteRange control;
-    uint32_t flags;
-    size_t realPayloadLength;
-    size_t realNameLength;
-  };
-
-  static bool parseRecvmsgMultishot(
-      ByteRange total,
-      struct msghdr const& msghdr,
-      ParsedRecvMsgMultishot& out);
-
-  EventRecvmsgMultishotCallback() = default;
-  virtual ~EventRecvmsgMultishotCallback() = default;
-
-  virtual Hdr* allocateRecvmsgMultishotData() noexcept = 0;
-};
-
-struct EventCallback {
-  enum class Type {
-    TYPE_NONE = 0,
-    TYPE_READ = 1,
-    TYPE_RECVMSG = 2,
-    TYPE_RECVMSG_MULTISHOT = 3
-  };
-  Type type_{Type::TYPE_NONE};
-  union {
-    EventReadCallback* readCb_;
-    EventRecvmsgCallback* recvmsgCb_;
-    EventRecvmsgMultishotCallback* recvmsgMultishotCb_;
-  };
-
-  void set(EventReadCallback* cb) {
-    type_ = Type::TYPE_READ;
-    readCb_ = cb;
-  }
-
-  void set(EventRecvmsgCallback* cb) {
-    type_ = Type::TYPE_RECVMSG;
-    recvmsgCb_ = cb;
-  }
-
-  void set(EventRecvmsgMultishotCallback* cb) {
-    type_ = Type::TYPE_RECVMSG_MULTISHOT;
-    recvmsgMultishotCb_ = cb;
-  }
-
-  void reset() { type_ = Type::TYPE_NONE; }
-};
-
 class EventBaseEvent {
  public:
   EventBaseEvent() = default;
@@ -167,16 +67,6 @@
     freeFn_ = freeFn;
   }
 
-  void setCallback(EventReadCallback* cb) { cb_.set(cb); }
-
-  void setCallback(EventRecvmsgCallback* cb) { cb_.set(cb); }
-
-  void setCallback(EventRecvmsgMultishotCallback* cb) { cb_.set(cb); }
-
-  void resetCallback() { cb_.reset(); }
-
-  const EventCallback& getCallback() const { return cb_; }
-
   void eb_event_set(
       libevent_fd_t fd,
       short events,
@@ -215,7 +105,6 @@
   EventBase* evb_{nullptr};
   void* userData_{nullptr};
   FreeFunction freeFn_{nullptr};
-  EventCallback cb_;
 };
 
 class EventBaseBackendBase {
diff --git a/folly/folly/io/async/EventBaseLocal.cpp b/folly/folly/io/async/EventBaseLocal.cpp
--- a/folly/folly/io/async/EventBaseLocal.cpp
+++ b/folly/folly/io/async/EventBaseLocal.cpp
@@ -19,7 +19,6 @@
 #include <atomic>
 #include <thread>
 
-#include <folly/MapUtil.h>
 #include <folly/Memory.h>
 
 namespace folly {
diff --git a/folly/folly/io/async/EventBasePoller.cpp b/folly/folly/io/async/EventBasePoller.cpp
--- a/folly/folly/io/async/EventBasePoller.cpp
+++ b/folly/folly/io/async/EventBasePoller.cpp
@@ -651,9 +651,10 @@
       return std::make_unique<EventBasePollerIoUring>();
     }
 #endif
-    throw std::invalid_argument(fmt::format(
-        "Unsupported EventBasePoller backend: {}",
-        FLAGS_folly_event_base_poller_backend));
+    throw std::invalid_argument(
+        fmt::format(
+            "Unsupported EventBasePoller backend: {}",
+            FLAGS_folly_event_base_poller_backend));
   }();
   return *instance;
 }
diff --git a/folly/folly/io/async/EventBaseThread.cpp b/folly/folly/io/async/EventBaseThread.cpp
--- a/folly/folly/io/async/EventBaseThread.cpp
+++ b/folly/folly/io/async/EventBaseThread.cpp
@@ -16,7 +16,6 @@
 
 #include <folly/io/async/EventBaseThread.h>
 
-#include <folly/Memory.h>
 #include <folly/io/async/ScopedEventBaseThread.h>
 
 namespace folly {
diff --git a/folly/folly/io/async/EventHandler.cpp b/folly/folly/io/async/EventHandler.cpp
--- a/folly/folly/io/async/EventHandler.cpp
+++ b/folly/folly/io/async/EventHandler.cpp
@@ -151,6 +151,7 @@
   // this can't possibly fire if handler->eventBase_ is nullptr
   handler->eventBase_->bumpHandlingTime();
 
+  RequestContextSaverScopeGuard rctxGuard;
   ExecutionObserverScopeGuard guard(
       &handler->eventBase_->getExecutionObserverList(),
       &handler->eventBase_,
diff --git a/folly/folly/io/async/EventHandler.h b/folly/folly/io/async/EventHandler.h
--- a/folly/folly/io/async/EventHandler.h
+++ b/folly/folly/io/async/EventHandler.h
@@ -174,16 +174,6 @@
 
   bool isPending() const;
 
-  void setEventCallback(EventReadCallback* cb) { event_.setCallback(cb); }
-
-  void setEventCallback(EventRecvmsgCallback* cb) { event_.setCallback(cb); }
-
-  void setRecvmsgMultishotCallback(EventRecvmsgMultishotCallback* cb) {
-    event_.setCallback(cb);
-  }
-
-  void resetEventCallback() { event_.resetCallback(); }
-
   /*
    * If supported by the backend updates the event to be edge-triggered.
    * Returns true iff the update was successful.
diff --git a/folly/folly/io/async/EventUtil.h b/folly/folly/io/async/EventUtil.h
--- a/folly/folly/io/async/EventUtil.h
+++ b/folly/folly/io/async/EventUtil.h
@@ -30,26 +30,26 @@
 #define FOLLY_LIBEVENT_COMPAT_PLUCK2(name) \
   ev_evcallback.evcb_cb_union.evcb_##name
 #endif
-#define FOLLY_LIBEVENT_DEF_ACCESSORS(name)                           \
-  inline auto event_ref_##name(struct event* ev)                     \
-      ->decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name))) {  \
-    return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name));          \
-  }                                                                  \
-  inline auto event_ref_##name(struct event const* ev)               \
-      ->decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name))) { \
-    return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name));         \
-  }                                                                  \
-  //
-
-#define FOLLY_LIBEVENT_DEF_ACCESSORS2(name)                           \
+#define FOLLY_LIBEVENT_DEF_ACCESSORS(name)                            \
   inline auto event_ref_##name(struct event* ev)                      \
-      ->decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) {  \
-    return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name));          \
+      -> decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name))) {  \
+    return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name));           \
   }                                                                   \
   inline auto event_ref_##name(struct event const* ev)                \
-      ->decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) { \
-    return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name));         \
+      -> decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name))) { \
+    return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name));          \
   }                                                                   \
+  //
+
+#define FOLLY_LIBEVENT_DEF_ACCESSORS2(name)                            \
+  inline auto event_ref_##name(struct event* ev)                       \
+      -> decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) {  \
+    return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name));           \
+  }                                                                    \
+  inline auto event_ref_##name(struct event const* ev)                 \
+      -> decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) { \
+    return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name));          \
+  }                                                                    \
   //
 
 FOLLY_LIBEVENT_DEF_ACCESSORS(arg)
diff --git a/folly/folly/io/async/HHWheelTimer.cpp b/folly/folly/io/async/HHWheelTimer.cpp
--- a/folly/folly/io/async/HHWheelTimer.cpp
+++ b/folly/folly/io/async/HHWheelTimer.cpp
@@ -18,8 +18,6 @@
 
 #include <cassert>
 
-#include <folly/Memory.h>
-#include <folly/Optional.h>
 #include <folly/ScopeGuard.h>
 #include <folly/container/BitIterator.h>
 #include <folly/io/async/Request.h>
diff --git a/folly/folly/io/async/HHWheelTimer.h b/folly/folly/io/async/HHWheelTimer.h
--- a/folly/folly/io/async/HHWheelTimer.h
+++ b/folly/folly/io/async/HHWheelTimer.h
@@ -204,9 +204,8 @@
     std::chrono::steady_clock::time_point expiration_{};
     int bucket_{-1};
 
-    typedef boost::intrusive::
-        list<Callback, boost::intrusive::constant_time_size<false>>
-            List;
+    using List = boost::intrusive::
+        list<Callback, boost::intrusive::constant_time_size<false>>;
 
     std::shared_ptr<RequestContext> requestContext_;
 
@@ -334,7 +333,7 @@
   static constexpr unsigned int WHEEL_MASK = (WHEEL_SIZE - 1);
   static constexpr uint32_t LARGEST_SLOT = 0xffffffffUL;
 
-  typedef typename Callback::List CallbackList;
+  using CallbackList = typename Callback::List;
   CallbackList buckets_[WHEEL_BUCKETS][WHEEL_SIZE];
   std::array<std::size_t, (WHEEL_SIZE / sizeof(std::size_t)) / 8> bitmap_;
 
diff --git a/folly/folly/io/async/IoUring.cpp b/folly/folly/io/async/IoUring.cpp
--- a/folly/folly/io/async/IoUring.cpp
+++ b/folly/folly/io/async/IoUring.cpp
@@ -18,15 +18,12 @@
 
 #include <cerrno>
 #include <ostream>
-#include <stdexcept>
 #include <string>
 
-#include <boost/intrusive/parent_from_member.hpp>
 #include <fmt/ostream.h>
 #include <glog/logging.h>
 
 #include <folly/Exception.h>
-#include <folly/Likely.h>
 #include <folly/String.h>
 #include <folly/portability/Unistd.h>
 
diff --git a/folly/folly/io/async/IoUringBackend.cpp b/folly/folly/io/async/IoUringBackend.cpp
--- a/folly/folly/io/async/IoUringBackend.cpp
+++ b/folly/folly/io/async/IoUringBackend.cpp
@@ -78,7 +78,7 @@
 
 struct SignalRegistry {
   struct SigInfo {
-    struct sigaction sa_ {};
+    struct sigaction sa_{};
     size_t refs_{0};
   };
   using SignalMap = std::map<int, SigInfo>;
@@ -110,7 +110,7 @@
     int fd = notifyFd_.load();
     if (fd >= 0) {
       uint8_t sigNum = static_cast<uint8_t>(sig);
-      fileops::write(fd, &sigNum, 1);
+      std::ignore = fileops::write(fd, &sigNum, 1);
     }
   }
 }
@@ -327,14 +327,13 @@
 
 template <class... Args>
 IoUringProvidedBufferRing::UniquePtr makeProvidedBufferRing(Args&&... args) {
-  return IoUringProvidedBufferRing::UniquePtr(
-      new IoUringProvidedBufferRing(std::forward<Args>(args)...));
+  return IoUringProvidedBufferRing::create(std::forward<Args>(args)...);
 }
 
 #else
 
 template <class... Args>
-IoUringBufferProviderBase::UniquePtr makeProvidedBufferRing(Args&&...) {
+IoUringProvidedBufferRing::UniquePtr makeProvidedBufferRing(Args&&...) {
   throw IoUringBackend::NotAvailable(
       "Provided buffer rings not compiled into this binary");
 }
@@ -344,7 +343,8 @@
 bool validateZeroCopyRxOptions(IoUringBackend::Options& options) {
   if (options.zeroCopyRx &&
       (options.zcRxIfname.empty() || options.zcRxIfindex <= 0 ||
-       options.zcRxQueueId == -1 || !options.resolveNapiId)) {
+       options.zcRxQueueId == -1 || !options.resolveNapiId ||
+       !options.srcPortQueueId)) {
     return false;
   }
 
@@ -523,9 +523,9 @@
 }
 
 IoUringBackend::IoUringBackend(Options options)
-    : options_(options),
-      numEntries_(options.capacity),
-      fdRegistry_(ioRing_, options.registeredFds) {
+    : options_(std::move(options)),
+      numEntries_(options_.capacity),
+      fdRegistry_(ioRing_, options_.registeredFds) {
   // create the timer fd
   timerFd_ = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
   if (timerFd_ < 0) {
@@ -539,7 +539,7 @@
   ::memset(&params_, 0, sizeof(params_));
 
   params_.flags |= IORING_SETUP_CQSIZE;
-  params_.cq_entries = options.capacity;
+  params_.cq_entries = options_.capacity;
 
 #if FOLLY_IO_URING_UP_TO_DATE
   if (options_.taskRunCoop) {
@@ -562,13 +562,14 @@
     params_.flags |= IORING_SETUP_COOP_TASKRUN;
     params_.flags |= IORING_SETUP_SUBMIT_ALL;
 
-    napiId_ = options_.resolveNapiId(options.zcRxIfindex, options.zcRxQueueId);
+    napiId_ =
+        options_.resolveNapiId(options_.zcRxIfindex, options_.zcRxQueueId);
   }
 
   // poll SQ options
-  if (options.flags & Options::Flags::POLL_SQ) {
+  if (options_.flags & Options::Flags::POLL_SQ) {
     params_.flags |= IORING_SETUP_SQPOLL;
-    params_.sq_thread_idle = options.sqIdle.count();
+    params_.sq_thread_idle = options_.sqIdle.count();
   }
 
   SQGroupInfoRegistry::FDCreateFunc func = [&](struct io_uring_params& params) {
@@ -578,16 +579,17 @@
           options_.sqeSize > 0 ? options_.sqeSize : 2 * options_.maxSubmit;
       int ret = ::io_uring_queue_init_params(sqeSize, &ioRing_, &params);
       if (ret) {
-        options.capacity /= 2;
-        if (options.minCapacity && (options.capacity >= options.minCapacity)) {
+        options_.capacity /= 2;
+        if (options_.minCapacity &&
+            (options_.capacity >= options_.minCapacity)) {
           LOG(INFO)
               << "io_uring_queue_init_params(" << 2 * options_.maxSubmit << ","
               << params.cq_entries << ") " << "failed errno = " << errno
               << ":\"" << folly::errnoStr(errno) << "\" " << this
-              << " retrying with capacity = " << options.capacity;
+              << " retrying with capacity = " << options_.capacity;
 
-          params_.cq_entries = options.capacity;
-          numEntries_ = options.capacity;
+          params_.cq_entries = options_.capacity;
+          numEntries_ = options_.capacity;
         } else {
           LOG(ERROR) << "io_uring_queue_init_params(" << 2 * options_.maxSubmit
                      << "," << params.cq_entries << ") " << "failed ret = "
@@ -612,7 +614,7 @@
       options_.sqGroupNumThreads,
       func,
       params_,
-      options.sqCpus);
+      options_.sqCpus);
 
   if (!options_.sqGroupName.empty()) {
     LOG(INFO) << "Adding to SQ poll group \"" << options_.sqGroupName
@@ -730,7 +732,7 @@
     try {
       Options options;
       options.setCapacity(1024);
-      IoUringBackend backend(options);
+      IoUringBackend backend(std::move(options));
     } catch (const NotAvailable&) {
       sAvailable = false;
     }
@@ -806,12 +808,12 @@
 
 void IoUringBackend::addTimerEvent(
     Event& event, const struct timeval* timeout) {
-  auto getTimerExpireTime = [](const auto& timeout) {
+  auto getTimerExpireTime = [](const auto& timeout2) {
     using namespace std::chrono;
     auto now = steady_clock::now();
 
-    auto us = duration_cast<microseconds>(seconds(timeout.tv_sec)) +
-        microseconds(timeout.tv_usec);
+    auto us = duration_cast<microseconds>(seconds(timeout2.tv_sec)) +
+        microseconds(timeout2.tv_usec);
     return now + us;
   };
 
@@ -935,20 +937,19 @@
   return ret;
 }
 
-IoUringBackend::IoSqe* IoUringBackend::allocIoSqe(const EventCallback& cb) {
+IoUringBackend::IoSqe* IoUringBackend::allocIoSqe() {
   // try to allocate from the pool first
-  if ((cb.type_ == EventCallback::Type::TYPE_NONE) && (!freeList_.empty())) {
+  if (!freeList_.empty()) {
     auto* ret = &freeList_.front();
     freeList_.pop_front();
     return ret;
   }
 
   // alloc a new IoSqe
-  return allocNewIoSqe(cb);
+  return allocNewIoSqe();
 }
 
 void IoUringBackend::releaseIoSqe(IoUringBackend::IoSqe* aioIoSqe) noexcept {
-  aioIoSqe->cbData_.releaseData();
   // unregister the file dsecriptor record
   if (aioIoSqe->fdRecord_) {
     unregisterFd(aioIoSqe->fdRecord_);
@@ -976,9 +977,6 @@
       SCOPE_EXIT {
         ioSqe->useCount_--;
       };
-      if (ioSqe->cbData_.processCb(this, res, flags)) {
-        return;
-      }
     }
 
     // if this is not a persistent event
@@ -1024,15 +1022,13 @@
       bool inserted = (event_ref_flags(ev) & EVLIST_INSERTED);
       // prevent the callback from freeing the aioIoSqe
       ioSqe->useCount_++;
-      if (!ioSqe->cbData_.processCb(this, ioSqe->res_, ioSqe->cqeFlags_)) {
-        // adjust the ev_res for the poll case
-        ev->ev_res = getPollEvents(ioSqe->res_, ev->ev_events);
-        // handle spurious poll events that return 0
-        // this can happen during high load on process startup
-        if (ev->ev_res) {
-          (*event_ref_callback(ev))(
-              (int)ev->ev_fd, ev->ev_res, event_ref_arg(ev));
-        }
+      // adjust the ev_res for the poll case
+      ev->ev_res = getPollEvents(ioSqe->res_, ev->ev_events);
+      // handle spurious poll events that return 0
+      // this can happen during high load on process startup
+      if (ev->ev_res) {
+        (*event_ref_callback(ev))(
+            (int)ev->ev_fd, ev->ev_res, event_ref_arg(ev));
       }
       // get the event again
       event = ioSqe->event_;
@@ -1101,28 +1097,21 @@
   }
 
   if (options_.initialProvidedBuffersCount) {
-    auto get_shift = [](int x) -> int {
-      int shift = findLastSet(x) - 1;
-      if (x != (1 << shift)) {
-        shift++;
-      }
-      return shift;
-    };
-
-    int sizeShift =
-        std::max<int>(get_shift(options_.initialProvidedBuffersEachSize), 5);
-    int ringShift =
-        std::max<int>(get_shift(options_.initialProvidedBuffersCount), 1);
-
     try {
       IoUringProvidedBufferRing::Options options = {
           .gid = nextBufferProviderGid(),
-          .count = options_.initialProvidedBuffersCount,
-          .bufferShift = sizeShift,
-          .ringSizeShift = ringShift,
-          .useHugePages = false,
+          .bufferCount =
+              static_cast<uint32_t>(options_.initialProvidedBuffersCount),
+          .bufferSize =
+              static_cast<uint32_t>(options_.initialProvidedBuffersEachSize),
+          .useHugePages = options_.useHugePages,
+          .useIncrementalBuffers = options_.enableIncrementalBuffers,
       };
-      bufferProvider_ = makeProvidedBufferRing(this->ioRingPtr(), options);
+      for (size_t i = 0; i < options_.providedBufRings; i++) {
+        bufferProviders_.push_back(
+            makeProvidedBufferRing(this->ioRingPtr(), options));
+        options.gid = nextBufferProviderGid();
+      }
     } catch (const IoUringProvidedBufferRing::LibUringCallError& ex) {
       LOG(ERROR) << folly::to<std::string>(
           "failed to make provided buffer ring, buffer count: ",
@@ -1134,15 +1123,20 @@
   }
 
   if (options_.zeroCopyRx) {
-    IoUringZeroCopyBufferPool::Params params = {
-        .ring = this->ioRingPtr(),
-        .numPages = static_cast<size_t>(options_.zcRxNumPages),
-        .pageSize = kZeroCopyPageSize,
-        .rqEntries = static_cast<uint32_t>(options_.zcRxRefillEntries),
-        .ifindex = static_cast<uint32_t>(options_.zcRxIfindex),
-        .queueId = static_cast<uint16_t>(options_.zcRxQueueId),
-    };
-    zcBufferPool_ = IoUringZeroCopyBufferPool::create(params);
+    if (options_.bufferPoolHandle.has_value()) {
+      zcBufferPool_ = IoUringZeroCopyBufferPool::importHandle(
+          std::move(options_.bufferPoolHandle.value()), this->ioRingPtr());
+    } else {
+      IoUringZeroCopyBufferPool::Params params = {
+          .ring = this->ioRingPtr(),
+          .numPages = static_cast<size_t>(options_.zcRxNumPages),
+          .pageSize = kZeroCopyPageSize,
+          .rqEntries = static_cast<uint32_t>(options_.zcRxRefillEntries),
+          .ifindex = static_cast<uint32_t>(options_.zcRxIfindex),
+          .queueId = static_cast<uint16_t>(options_.zcRxQueueId),
+      };
+      zcBufferPool_ = IoUringZeroCopyBufferPool::create(params);
+    }
   }
 }
 
@@ -1169,6 +1163,10 @@
     initSubmissionLinked();
   }
 
+  if (useReqBatching()) {
+    ::io_uring_set_iowait(&ioRing_, false);
+  }
+
   if (options_.registerRingFd) {
     // registering just has some perf impact, so no need to fall back
 #if FOLLY_IO_URING_UP_TO_DATE
@@ -1177,6 +1175,16 @@
     }
 #endif
   }
+
+  if (options_.arenaIndex > 0) {
+    int ret = ::io_uring_register_buffers(&ioRing_, &options_.arenaRegion, 1);
+    if (ret < 0) {
+      throw NotAvailable(
+          fmt::format(
+              "io_uring_register_buffers failed: {}", folly::errnoStr(-ret)));
+    }
+  }
+
   if (!addTimerFd() || !addSignalFds()) {
     cleanup();
     throw NotAvailable("io_uring_submit error");
@@ -1292,7 +1300,7 @@
 
   if ((ev->ev_events & (EV_READ | EV_WRITE)) &&
       !(event_ref_flags(ev) & (EVLIST_INSERTED | EVLIST_ACTIVE))) {
-    auto* ioSqe = allocIoSqe(event.getCallback());
+    auto* ioSqe = allocIoSqe();
     CHECK(ioSqe);
     ioSqe->event_ = &event;
     ioSqe->setEventBase(event.eb_ev_base());
@@ -1446,7 +1454,7 @@
 }
 
 int IoUringBackend::cancelOne(IoSqe* ioSqe) {
-  auto* rentry = static_cast<IoSqe*>(allocIoSqe(EventCallback()));
+  auto* rentry = static_cast<IoSqe*>(allocIoSqe());
   if (!rentry) {
     return 0;
   }
@@ -1467,9 +1475,13 @@
 size_t IoUringBackend::getActiveEvents(WaitForEventsMode waitForEvents) {
   struct io_uring_cqe* cqe;
 
-  setGetActiveEvents();
+  if (kIsDebug && gettingEvents_) {
+    throw std::runtime_error("getting events is not reentrant");
+  }
+  gettingEvents_ = true;
+
   SCOPE_EXIT {
-    doneGetActiveEvents();
+    gettingEvents_ = false;
   };
 
   auto inner_do_wait = [&]() -> int {
@@ -1764,9 +1776,7 @@
 
 void IoUringBackend::queueRead(
     int fd, void* buf, unsigned int nbytes, off_t offset, FileOpCallback&& cb) {
-  struct iovec iov {
-    buf, nbytes
-  };
+  struct iovec iov{buf, nbytes};
   auto* ioSqe = new ReadIoSqe(this, fd, &iov, offset, std::move(cb));
   ioSqe->backendCb_ = processFileOpCB;
 
@@ -1779,9 +1789,7 @@
     unsigned int nbytes,
     off_t offset,
     FileOpCallback&& cb) {
-  struct iovec iov {
-    const_cast<void*>(buf), nbytes
-  };
+  struct iovec iov{const_cast<void*>(buf), nbytes};
   auto* ioSqe = new WriteIoSqe(this, fd, &iov, offset, std::move(cb));
   ioSqe->backendCb_ = processFileOpCB;
 
@@ -1870,6 +1878,18 @@
   submitImmediateIoSqe(*ioSqe);
 }
 
+void IoUringBackend::queueUnlinkat(
+    int dirfd, const char* path, int flags, FileOpCallback&& cb) {
+  auto* ioSqe = new FUnlinkIoSqe(this, dirfd, path, flags, std::move(cb));
+  ioSqe->backendCb_ = processFileOpCB;
+
+  submitImmediateIoSqe(*ioSqe);
+}
+
+void IoUringBackend::queueUnlink(const char* path, FileOpCallback&& cb) {
+  queueUnlinkat(AT_FDCWD, path, 0, std::move(cb));
+}
+
 void IoUringBackend::queueFallocate(
     int fd, int mode, off_t offset, off_t len, FileOpCallback&& cb) {
   auto* ioSqe = new FAllocateIoSqe(this, fd, mode, offset, len, std::move(cb));
@@ -1906,6 +1926,16 @@
   submitImmediateIoSqe(*ioSqe);
 }
 
+int IoUringBackend::computeSrcPortForQueueId(
+    const folly::IPAddress& destAddr, uint16_t destPort) {
+  if (!options_.srcPortQueueId || napiId_ < 0) {
+    return -1;
+  }
+
+  return options_.srcPortQueueId(
+      destAddr, destPort, napiId_, options_.zcRxIfname.c_str());
+}
+
 void IoUringBackend::processFileOp(IoSqe* sqe, int res) noexcept {
   auto* ioSqe = reinterpret_cast<FileOpIoSqe*>(sqe);
   // save the res
@@ -1942,24 +1972,12 @@
   ioSqe->offset_ += cqe->res;
 }
 
-bool IoUringBackend::kernelHasNonBlockWriteFixes() const {
-#if FOLLY_IO_URING_UP_TO_DATE
-  // this was fixed in 5.18, which introduced linked file
-  // fixed in "io_uring: only wake when the correct events are set"
-  return params_.features & IORING_FEAT_LINKED_FILE;
-#else
-  // this indicates that sockets have to manually remove O_NONBLOCK
-  // which is a bit slower but shouldnt cause any functional changes
-  return false;
-#endif
-}
-
 namespace {
 
 static bool doKernelSupportsRecvmsgMultishot() {
   try {
     struct S : IoSqeBase {
-      explicit S(IoUringBufferProviderBase* bp) : bp_(bp) {
+      explicit S(IoUringProvidedBufferRing* bp) : bp_(bp) {
         fd = fileops::open("/dev/null", O_RDONLY);
         memset(&msg, 0, sizeof(msg));
       }
@@ -1983,7 +2001,7 @@
         delete this;
       }
 
-      IoUringBufferProviderBase* bp_;
+      IoUringProvidedBufferRing* bp_;
       bool supported = false;
       struct msghdr msg;
       int fd = -1;
@@ -1991,7 +2009,8 @@
 
     std::unique_ptr<S> s;
     IoUringBackend io(
-        IoUringBackend::Options().setInitialProvidedBuffers(1024, 1));
+        std::move(
+            IoUringBackend::Options().setInitialProvidedBuffers(1024, 1)));
     if (!io.bufferProvider()) {
       return false;
     }
diff --git a/folly/folly/io/async/IoUringBackend.h b/folly/folly/io/async/IoUringBackend.h
--- a/folly/folly/io/async/IoUringBackend.h
+++ b/folly/folly/io/async/IoUringBackend.h
@@ -33,12 +33,14 @@
 #include <folly/CppAttributes.h>
 #include <folly/ExceptionString.h>
 #include <folly/Function.h>
+#include <folly/IPAddress.h>
 #include <folly/Optional.h>
 #include <folly/Range.h>
 #include <folly/io/IOBuf.h>
 #include <folly/io/async/EventBase.h>
 #include <folly/io/async/EventBaseBackendBase.h>
 #include <folly/io/async/IoUringBase.h>
+#include <folly/io/async/IoUringProvidedBufferRing.h>
 #include <folly/io/async/IoUringZeroCopyBufferPool.h>
 #include <folly/io/async/Liburing.h>
 #include <folly/portability/Asm.h>
@@ -64,6 +66,11 @@
 
   using ResolveNapiIdCallback =
       std::function<int(int ifindex, uint32_t queueId)>;
+  using SrcPortForQueueIdCallback = std::function<int(
+      const folly::IPAddress& destAddr,
+      uint16_t destPort,
+      int targetNapiId,
+      const char* ifname)>;
 
   struct Options {
     enum Flags {
@@ -73,6 +80,11 @@
     };
 
     Options() = default;
+    Options(const Options&) = delete;
+    Options& operator=(const Options&) = delete;
+    Options(Options&&) = default;
+    Options& operator=(Options&&) = default;
+    ~Options() = default;
 
     Options& setCapacity(size_t v) {
       capacity = v;
@@ -164,6 +176,20 @@
       return *this;
     }
 
+    constexpr bool isPow2(uint64_t n) noexcept {
+      return n > 0 && !((n - 1) & n);
+    }
+
+    Options& setProvidedBufRings(size_t v) {
+      if (!isPow2(v)) {
+        throw std::runtime_error(
+            folly::to<std::string>(
+                "number of provided buffer rings must be a power of 2"));
+      }
+      providedBufRings = v;
+      return *this;
+    }
+
     Options& setRegisterRingFd(bool v) {
       registerRingFd = v;
 
@@ -204,11 +230,12 @@
       zcRxIfname = std::move(v);
       zcRxIfindex = ::if_nametoindex(zcRxIfname.c_str());
       if (zcRxIfindex == 0) {
-        throw std::runtime_error(folly::to<std::string>(
-            "invalid network interface name: ",
-            zcRxIfname,
-            ", errno: ",
-            errno));
+        throw std::runtime_error(
+            folly::to<std::string>(
+                "invalid network interface name: ",
+                zcRxIfname,
+                ", errno: ",
+                errno));
       }
 
       return *this;
@@ -226,6 +253,12 @@
       return *this;
     }
 
+    Options& setZcrxSrcPortCallback(SrcPortForQueueIdCallback&& v) {
+      srcPortQueueId = std::move(v);
+
+      return *this;
+    }
+
     Options& setZeroCopyRxNumPages(int v) {
       zcRxNumPages = v;
 
@@ -238,6 +271,36 @@
       return *this;
     }
 
+    Options& setEnableIncrementalBuffers(bool v) {
+      enableIncrementalBuffers = v;
+
+      return *this;
+    }
+
+    Options& setUseHugePages(bool v) {
+      useHugePages = v;
+
+      return *this;
+    }
+
+    Options& setNativeAsyncSocketSupport(bool v) {
+      nativeAsyncSocketSupport = v;
+
+      return *this;
+    }
+
+    Options& setBufferPoolHandle(IoUringZeroCopyBufferPool::ExportHandle&& v) {
+      bufferPoolHandle = std::move(v);
+      return *this;
+    }
+
+    Options& setArenaRegion(void* base, size_t size, uint32_t index) {
+      arenaRegion.iov_base = base;
+      arenaRegion.iov_len = size;
+      arenaIndex = index;
+      return *this;
+    }
+
     ssize_t sqeSize{-1};
 
     size_t capacity{256};
@@ -248,6 +311,7 @@
     size_t sqGroupNumThreads{1};
     size_t initialProvidedBuffersCount{0};
     size_t initialProvidedBuffersEachSize{0};
+    size_t providedBufRings{1};
 
     uint32_t flags{0};
 
@@ -276,14 +340,29 @@
     int zcRxQueueId{-1};
     int zcRxIfindex{-1};
     ResolveNapiIdCallback resolveNapiId;
+    SrcPortForQueueIdCallback srcPortQueueId;
     int zcRxNumPages{-1};
     int zcRxRefillEntries{-1};
+
+    // Incremental Buffers
+    bool enableIncrementalBuffers{false};
+    bool useHugePages{false};
+
+    bool nativeAsyncSocketSupport{false};
+
+    std::optional<IoUringZeroCopyBufferPool::ExportHandle> bufferPoolHandle =
+        std::nullopt;
+
+    struct iovec arenaRegion{};
+    uint32_t arenaIndex{0};
   };
 
   explicit IoUringBackend(Options options);
   ~IoUringBackend() override;
   Options const& options() const { return options_; }
 
+  uint32_t getArenaIndex() const { return options_.arenaIndex; }
+
   bool isWaitingToSubmit() const {
     return waitingToSubmit_ || !submitList_.empty();
   }
@@ -292,7 +371,11 @@
   bool useReqBatching() const {
     return options_.timeout.count() > 0 && options_.batchSize > 0;
   }
+  bool supportAsyncSocket() { return options_.nativeAsyncSocketSupport; }
 
+  int computeSrcPortForQueueId(
+      const folly::IPAddress& destAddr, uint16_t destPort);
+
   // from EventBaseBackendBase
   int getPollableFd() const override { return ioRing_.ring_fd; }
   int getNapiId() const override { return napiId_; }
@@ -317,7 +400,6 @@
   // returns true if the current Linux kernel version
   // supports the io_uring backend
   static bool isAvailable();
-  bool kernelHasNonBlockWriteFixes() const;
   static bool kernelSupportsRecvmsgMultishot();
   static bool kernelSupportsDeferTaskrun();
   static bool kernelSupportsSendZC();
@@ -395,6 +477,11 @@
   void queueRename(
       const char* oldPath, const char* newPath, FileOpCallback&& cb);
 
+  void queueUnlinkat(
+      int dirfd, const char* path, int flags, FileOpCallback&& cb);
+
+  void queueUnlink(const char* path, FileOpCallback&& cb);
+
   void queueFallocate(
       int fd, int mode, off_t offset, off_t len, FileOpCallback&& cb);
 
@@ -419,7 +506,12 @@
   void cancel(IoSqeBase* sqe);
 
   // built in buffer provider
-  IoUringBufferProviderBase* bufferProvider() { return bufferProvider_.get(); }
+  IoUringProvidedBufferRing* bufferProvider() {
+    return bufferProviders_
+        [bufferProviderIdx_++ & (bufferProviders_.size() - 1)]
+            .get();
+  }
+  bool hasBufferProvider() { return bufferProviders_.size() > 0; }
   uint16_t nextBufferProviderGid() { return bufferProviderGidNext_++; }
   IoUringZeroCopyBufferPool* zcBufferPool() { return zcBufferPool_.get(); }
 
@@ -440,7 +532,7 @@
     int writeFd() const { return fds_[0]; }
 
    private:
-    std::array<int, 2> fds_{-1, -1};
+    std::array<int, 2> fds_{{-1, -1}};
   };
 
   struct UserData {
@@ -514,7 +606,7 @@
 
   void processPollIo(IoSqe* ioSqe, int res, uint32_t flags) noexcept;
 
-  IoSqe* FOLLY_NULLABLE allocIoSqe(const EventCallback& cb);
+  IoSqe* FOLLY_NULLABLE allocIoSqe();
   void releaseIoSqe(IoSqe* aioIoSqe) noexcept;
 
   // submit immediate if POLL_SQ | POLL_SQ_IMMEDIATE_IO flags are set
@@ -589,139 +681,12 @@
     void processSubmit(struct io_uring_sqe* sqe) noexcept override {
       auto* ev = event_->getEvent();
       if (ev) {
-        const auto& cb = event_->getCallback();
-        switch (cb.type_) {
-          case EventCallback::Type::TYPE_NONE:
-            break;
-          case EventCallback::Type::TYPE_READ:
-            if (auto* iov = cb.readCb_->allocateData()) {
-              prepRead(
-                  sqe,
-                  ev->ev_fd,
-                  &iov->data_,
-                  0,
-                  (ev->ev_events & EV_PERSIST) != 0);
-              cbData_.set(iov);
-              return;
-            }
-            break;
-          case EventCallback::Type::TYPE_RECVMSG:
-            if (auto* msg = cb.recvmsgCb_->allocateData()) {
-              prepRecvmsg(
-                  sqe,
-                  ev->ev_fd,
-                  &msg->data_,
-                  (ev->ev_events & EV_PERSIST) != 0);
-              cbData_.set(msg);
-              return;
-            }
-            break;
-          case EventCallback::Type::TYPE_RECVMSG_MULTISHOT:
-            if (auto* hdr =
-                    cb.recvmsgMultishotCb_->allocateRecvmsgMultishotData()) {
-              prepRecvmsgMultishot(sqe, ev->ev_fd, &hdr->data_);
-              cbData_.set(hdr);
-              return;
-            }
-            break;
-        }
         prepPollAdd(sqe, ev->ev_fd, getPollFlags(ev->ev_events));
       }
     }
 
     virtual void processActive() {}
 
-    struct EventCallbackData {
-      EventCallback::Type type_{EventCallback::Type::TYPE_NONE};
-      union {
-        EventReadCallback::IoVec* ioVec_;
-        EventRecvmsgCallback::MsgHdr* msgHdr_;
-        EventRecvmsgMultishotCallback::Hdr* hdr_;
-      };
-
-      void set(EventReadCallback::IoVec* ioVec) {
-        type_ = EventCallback::Type::TYPE_READ;
-        ioVec_ = ioVec;
-      }
-
-      void set(EventRecvmsgCallback::MsgHdr* msgHdr) {
-        type_ = EventCallback::Type::TYPE_RECVMSG;
-        msgHdr_ = msgHdr;
-      }
-
-      void set(EventRecvmsgMultishotCallback::Hdr* hdr) {
-        type_ = EventCallback::Type::TYPE_RECVMSG_MULTISHOT;
-        hdr_ = hdr;
-      }
-
-      void reset() { type_ = EventCallback::Type::TYPE_NONE; }
-
-      bool processCb(IoUringBackend* backend, int res, uint32_t flags) {
-        bool ret = false;
-        bool released = false;
-        switch (type_) {
-          case EventCallback::Type::TYPE_READ: {
-            released = ret = true;
-            auto cbFunc = ioVec_->cbFunc_;
-            cbFunc(ioVec_, res);
-            break;
-          }
-          case EventCallback::Type::TYPE_RECVMSG: {
-            released = ret = true;
-            auto cbFunc = msgHdr_->cbFunc_;
-            cbFunc(msgHdr_, res);
-            break;
-          }
-          case EventCallback::Type::TYPE_RECVMSG_MULTISHOT: {
-            ret = true;
-            std::unique_ptr<IOBuf> buf;
-            if (flags & IORING_CQE_F_BUFFER) {
-              if (IoUringBufferProviderBase* bp = backend->bufferProvider()) {
-                buf = bp->getIoBuf(flags >> 16, res);
-              }
-            }
-            hdr_->cbFunc_(hdr_, res, std::move(buf));
-            if (!(flags & IORING_CQE_F_MORE)) {
-              hdr_->freeFunc_(hdr_);
-              released = true;
-            }
-            break;
-          }
-          case EventCallback::Type::TYPE_NONE:
-            break;
-        }
-
-        if (released) {
-          type_ = EventCallback::Type::TYPE_NONE;
-        }
-
-        return ret;
-      }
-
-      void releaseData() {
-        switch (type_) {
-          case EventCallback::Type::TYPE_READ: {
-            auto freeFunc = ioVec_->freeFunc_;
-            freeFunc(ioVec_);
-            break;
-          }
-          case EventCallback::Type::TYPE_RECVMSG: {
-            auto freeFunc = msgHdr_->freeFunc_;
-            freeFunc(msgHdr_);
-            break;
-          }
-          case EventCallback::Type::TYPE_RECVMSG_MULTISHOT:
-            hdr_->freeFunc_(hdr_);
-            break;
-          case EventCallback::Type::TYPE_NONE:
-            break;
-        }
-        type_ = EventCallback::Type::TYPE_NONE;
-      }
-    };
-
-    EventCallbackData cbData_;
-
     void prepPollAdd(
         struct io_uring_sqe* sqe, int fd, uint32_t events) noexcept {
       CHECK(sqe);
@@ -796,7 +761,7 @@
         struct io_uring_sqe* sqe, int fd, struct msghdr* msg) noexcept {
       CHECK(sqe);
       ::io_uring_prep_recvmsg_multishot(sqe, fd, msg, MSG_TRUNC);
-      if (IoUringBufferProviderBase* bp = backend_->bufferProvider()) {
+      if (IoUringProvidedBufferRing* bp = backend_->bufferProvider()) {
         sqe->buf_group = bp->gid();
         sqe->flags |= IOSQE_BUFFER_SELECT;
       }
@@ -1010,6 +975,25 @@
 
     const char* oldPath_;
     const char* newPath_;
+  };
+
+  struct FUnlinkIoSqe : public FileOpIoSqe {
+    FUnlinkIoSqe(
+        IoUringBackend* backend,
+        int dirfd,
+        const char* path,
+        int flags,
+        FileOpCallback&& cb)
+        : FileOpIoSqe(backend, dirfd, std::move(cb)),
+          path_(path),
+          flags_(flags) {}
+
+    void processSubmit(struct io_uring_sqe* sqe) noexcept override {
+      ::io_uring_prep_unlinkat(sqe, fd_, path_, flags_);
+      ::io_uring_sqe_set_data(sqe, this);
+    }
+
+    const char* path_;
     int flags_;
   };
 
@@ -1107,7 +1091,7 @@
     backend->processRecvZc(ioSqe, cqe);
   }
 
-  IoUringBackend::IoSqe* allocNewIoSqe(const EventCallback& /*cb*/) {
+  IoUringBackend::IoSqe* allocNewIoSqe() {
     // allow pool alloc if numPooledIoSqeInUse_ < numEntries_
     auto* ret = new IoSqe(this, numPooledIoSqeInUse_ < numEntries_);
     ++numPooledIoSqeInUse_;
@@ -1150,7 +1134,8 @@
   // submit
   IoSqeBaseList submitList_;
   uint16_t bufferProviderGidNext_{0};
-  IoUringBufferProviderBase::UniquePtr bufferProvider_;
+  std::vector<IoUringProvidedBufferRing::UniquePtr> bufferProviders_;
+  uint64_t bufferProviderIdx_{0};
   IoUringZeroCopyBufferPool::UniquePtr zcBufferPool_;
 
   // loop related
@@ -1186,13 +1171,6 @@
   void dCheckSubmitTid();
   void setSubmitting() noexcept { isSubmitting_++; }
   void doneSubmitting() noexcept { isSubmitting_--; }
-  void setGetActiveEvents() {
-    if (kIsDebug && gettingEvents_) {
-      throw std::runtime_error("getting events is not reentrant");
-      gettingEvents_ = true;
-    }
-  }
-  void doneGetActiveEvents() noexcept { gettingEvents_ = false; }
   bool isSubmitting() const noexcept { return isSubmitting_; }
 };
 
diff --git a/folly/folly/io/async/IoUringBase.h b/folly/folly/io/async/IoUringBase.h
--- a/folly/folly/io/async/IoUringBase.h
+++ b/folly/folly/io/async/IoUringBase.h
@@ -64,55 +64,18 @@
   // This is used if you want to prepare this sqe for reuse, but will manage the
   // lifetime. For example for zerocopy send, you might want to reuse the sqe
   // but still have a notification inbound.
-  void prepareForReuse() { internalUnmarkInflight(); }
+  void prepareForReuse() { internalMarkInflight(false); }
+  void internalMarkInflight(bool val) { inFlight_ = val; }
 
  private:
   friend class IoUringBackend;
   void internalSubmit(struct io_uring_sqe* sqe) noexcept;
   void internalCallback(const io_uring_cqe* cqe) noexcept;
-  void internalUnmarkInflight() { inFlight_ = false; }
 
   bool inFlight_ = false;
   bool cancelled_ = false;
   EventBase* evb_ = nullptr;
   Type type_;
-};
-
-class IoUringBufferProviderBase {
- protected:
-  uint16_t const gid_;
-  size_t const sizePerBuffer_;
-
- public:
-  struct Deleter {
-    void operator()(IoUringBufferProviderBase* base) {
-      if (base) {
-        base->destroy();
-      }
-    }
-  };
-
-  using UniquePtr = std::unique_ptr<IoUringBufferProviderBase, Deleter>;
-  explicit IoUringBufferProviderBase(uint16_t gid, size_t sizePerBuffer)
-      : gid_(gid), sizePerBuffer_(sizePerBuffer) {}
-  virtual ~IoUringBufferProviderBase() = default;
-
-  IoUringBufferProviderBase(IoUringBufferProviderBase&&) = delete;
-  IoUringBufferProviderBase(IoUringBufferProviderBase const&) = delete;
-  IoUringBufferProviderBase& operator=(IoUringBufferProviderBase&&) = delete;
-  IoUringBufferProviderBase& operator=(IoUringBufferProviderBase const&) =
-      delete;
-
-  size_t sizePerBuffer() const { return sizePerBuffer_; }
-  uint16_t gid() const { return gid_; }
-
-  virtual uint32_t count() const noexcept = 0;
-  virtual void unusedBuf(uint16_t i) noexcept = 0;
-  virtual std::unique_ptr<IOBuf> getIoBuf(
-      uint16_t i, size_t length) noexcept = 0;
-  virtual void enobuf() noexcept = 0;
-  virtual bool available() const noexcept = 0;
-  virtual void destroy() noexcept = 0;
 };
 
 struct IoUringFdRegistrationRecord
diff --git a/folly/folly/io/async/IoUringConnect.cpp b/folly/folly/io/async/IoUringConnect.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/io/async/IoUringConnect.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/io/async/IoUringConnect.h>
+
+#include <folly/io/async/EventBase.h>
+#include <folly/io/async/IoUringBackend.h>
+
+namespace folly {
+
+#if FOLLY_HAS_LIBURING
+
+IoUringConnectHandle::UniquePtr IoUringConnectHandle::create(
+    EventBase* evb,
+    NetworkSocket fd,
+    IoUringConnectCallback* callback,
+    std::chrono::milliseconds timeout) {
+  auto* backend = dynamic_cast<folly::IoUringBackend*>(evb->getBackend());
+  if (!backend) {
+    return nullptr;
+  }
+
+  auto handle = std::make_unique<IoUringConnectHandle>(
+      NotPubliclyConstructible{}, evb, backend, fd, callback);
+  if (timeout.count() > 0) {
+    handle->scheduleTimeout(uint32_t(timeout.count()));
+  }
+  backend->submitSoon(*handle);
+  return handle;
+}
+
+IoUringConnectHandle::IoUringConnectHandle(
+    NotPubliclyConstructible,
+    EventBase* evb,
+    IoUringBackend* backend,
+    NetworkSocket fd,
+    IoUringConnectCallback* callback)
+    : IoSqeBase(IoSqeBase::Type::Connect),
+      AsyncTimeout(evb),
+      backend_(backend),
+      fd_(fd),
+      callback_(callback) {
+  setEventBase(evb);
+}
+
+void IoUringConnectHandle::processSubmit(struct io_uring_sqe* sqe) noexcept {
+  ::io_uring_prep_poll_add(sqe, fd_.toFd(), POLLOUT);
+}
+
+void IoUringConnectHandle::callback(
+    const struct io_uring_cqe* /*cqe*/) noexcept {
+  cancelTimeout();
+  callback_->connectSuccess();
+  return;
+}
+
+void IoUringConnectHandle::callbackCancelled(const io_uring_cqe*) noexcept {
+  delete this;
+}
+
+void IoUringConnectHandle::timeoutExpired() noexcept {
+  if (!cancelled()) {
+    CHECK(callback_ != nullptr);
+    callback_->connectTimeout();
+  }
+}
+
+bool IoUringConnectHandle::cancel() {
+  cancelTimeout();
+  if (inFlight()) {
+    backend_->cancel(this);
+    return true;
+  }
+
+  return false;
+}
+
+#else
+
+IoUringConnectHandle::UniquePtr IoUringConnectHandle::create(
+    EventBase* /*evb*/,
+    NetworkSocket /*fd*/,
+    IoUringConnectCallback* /*callback*/,
+    std::chrono::milliseconds /*timeout*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+IoUringConnectHandle::IoUringConnectHandle(
+    NotPubliclyConstructible,
+    EventBase* evb,
+    IoUringBackend* /*backend*/,
+    NetworkSocket /*fd*/,
+    IoUringConnectCallback* /*callback*/)
+    : IoSqeBase(IoSqeBase::Type::Connect),
+      AsyncTimeout(evb),
+      backend_(nullptr),
+      fd_(),
+      callback_(nullptr) {
+  (void)backend_;
+  (void)fd_;
+  (void)callback_;
+}
+
+void IoUringConnectHandle::processSubmit(
+    struct io_uring_sqe* /*sqe*/) noexcept {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringConnectHandle::callback(
+    const struct io_uring_cqe* /*cqe*/) noexcept {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringConnectHandle::callbackCancelled(const io_uring_cqe*) noexcept {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringConnectHandle::timeoutExpired() noexcept {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+bool IoUringConnectHandle::cancel() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+#endif
+
+} // namespace folly
diff --git a/folly/folly/io/async/IoUringConnect.h b/folly/folly/io/async/IoUringConnect.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/io/async/IoUringConnect.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/io/async/AsyncTimeout.h>
+#include <folly/io/async/IoUringBase.h>
+#include <folly/net/NetworkSocket.h>
+
+namespace folly {
+
+class IoUringConnectCallback {
+ public:
+  virtual ~IoUringConnectCallback() = default;
+
+  virtual void connectSuccess() = 0;
+  virtual void connectTimeout() = 0;
+};
+
+class EventBase;
+class IoUringBackend;
+
+class IoUringConnectHandle : public IoSqeBase, public AsyncTimeout {
+ private:
+  struct NotPubliclyConstructible {};
+
+ public:
+  using UniquePtr = std::unique_ptr<IoUringConnectHandle>;
+
+  static IoUringConnectHandle::UniquePtr create(
+      EventBase* evb,
+      NetworkSocket fd,
+      IoUringConnectCallback* callback,
+      std::chrono::milliseconds timeout);
+
+  IoUringConnectHandle(
+      NotPubliclyConstructible,
+      EventBase* evb,
+      IoUringBackend* backend,
+      NetworkSocket fd,
+      IoUringConnectCallback* callback);
+
+  /*
+   * IoSqeBase
+   */
+  void processSubmit(struct io_uring_sqe* sqe) noexcept override;
+  void callback(const struct io_uring_cqe* cqe) noexcept override;
+  void callbackCancelled(const io_uring_cqe*) noexcept override;
+
+  /*
+   * AsyncTimeout
+   */
+  void timeoutExpired() noexcept override;
+
+  bool cancel();
+
+ private:
+  IoUringBackend* backend_;
+  NetworkSocket fd_;
+  IoUringConnectCallback* callback_;
+};
+
+} // namespace folly
diff --git a/folly/folly/io/async/IoUringEvent.cpp b/folly/folly/io/async/IoUringEvent.cpp
--- a/folly/folly/io/async/IoUringEvent.cpp
+++ b/folly/folly/io/async/IoUringEvent.cpp
@@ -28,10 +28,8 @@
 }
 
 IoUringEvent::IoUringEvent(
-    folly::EventBase* eventBase,
-    IoUringBackend::Options const& o,
-    bool use_event_fd)
-    : EventHandler(eventBase), eventBase_(eventBase), backend_(o) {
+    folly::EventBase* eventBase, IoUringBackend::Options o, bool use_event_fd)
+    : EventHandler(eventBase), eventBase_(eventBase), backend_(std::move(o)) {
   if (use_event_fd) {
     eventFd_ = folly::File{eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC), true};
     int ret = io_uring_register_eventfd(backend_.ioRingPtr(), eventFd_->fd());
@@ -81,7 +79,7 @@
   } else if (eventFd_) {
     if (!edgeTriggered_) {
       uint64_t val;
-      fileops::read(eventFd_->fd(), &val, sizeof(val));
+      std::ignore = fileops::read(eventFd_->fd(), &val, sizeof(val));
     }
     backend_.loopPoll();
 
diff --git a/folly/folly/io/async/IoUringEvent.h b/folly/folly/io/async/IoUringEvent.h
--- a/folly/folly/io/async/IoUringEvent.h
+++ b/folly/folly/io/async/IoUringEvent.h
@@ -30,7 +30,7 @@
  public:
   IoUringEvent(
       folly::EventBase* eventBase,
-      IoUringBackend::Options const& o,
+      IoUringBackend::Options o,
       bool use_event_fd = true);
   ~IoUringEvent() override;
 
diff --git a/folly/folly/io/async/IoUringEventBaseLocal.cpp b/folly/folly/io/async/IoUringEventBaseLocal.cpp
--- a/folly/folly/io/async/IoUringEventBaseLocal.cpp
+++ b/folly/folly/io/async/IoUringEventBaseLocal.cpp
@@ -50,7 +50,7 @@
 }
 
 void IoUringEventBaseLocal::attach(
-    EventBase* evb, IoUringBackend::Options const& options, bool use_eventfd) {
+    EventBase* evb, IoUringBackend::Options options, bool use_eventfd) {
   evb->dcheckIsInEventBaseThread();
 
   // We tie into event base callbacks which need to be cleaned up earlier than
@@ -66,7 +66,8 @@
         "this event base already has a local io_uring attached");
   }
   local->emplace(
-      *evb, std::make_unique<IoUringEvent>(evb, options, use_eventfd));
+      *evb,
+      std::make_unique<IoUringEvent>(evb, std::move(options), use_eventfd));
 }
 
 } // namespace folly
diff --git a/folly/folly/io/async/IoUringEventBaseLocal.h b/folly/folly/io/async/IoUringEventBaseLocal.h
--- a/folly/folly/io/async/IoUringEventBaseLocal.h
+++ b/folly/folly/io/async/IoUringEventBaseLocal.h
@@ -27,9 +27,7 @@
 class IoUringEventBaseLocal {
  public:
   static void attach(
-      EventBase* evb,
-      IoUringBackend::Options const& options,
-      bool use_eventfd = true);
+      EventBase* evb, IoUringBackend::Options options, bool use_eventfd = true);
   static IoUringBackend* try_get(EventBase* evb);
 };
 
diff --git a/folly/folly/io/async/IoUringProvidedBufferRing.cpp b/folly/folly/io/async/IoUringProvidedBufferRing.cpp
--- a/folly/folly/io/async/IoUringProvidedBufferRing.cpp
+++ b/folly/folly/io/async/IoUringProvidedBufferRing.cpp
@@ -17,38 +17,68 @@
 #include <folly/io/async/IoUringProvidedBufferRing.h>
 
 #include <folly/Conv.h>
-#include <folly/ExceptionString.h>
 #include <folly/String.h>
 #include <folly/lang/Align.h>
+#include <folly/portability/SysMman.h>
 
 #if FOLLY_HAS_LIBURING
 
+namespace {
+constexpr uint32_t kMinBufferSize = 32;
+constexpr uint32_t kHugePageSizeBytes = 1024 * 1024 * 2;
+constexpr uint32_t kPageSizeBytes = 4096;
+constexpr uint32_t kBufferAlignBytes = 32;
+} // namespace
+
 namespace folly {
 
-IoUringProvidedBufferRing::ProvidedBuffersBuffer::ProvidedBuffersBuffer(
-    size_t count, int bufferShift, int ringCountShift, bool huge_pages)
-    : bufferShift_(bufferShift), bufferCount_(count) {
-  // space for the ring
-  int ringCount = 1 << ringCountShift;
-  ringMask_ = ringCount - 1;
-  ringMemSize_ = sizeof(struct io_uring_buf) * ringCount;
+void IoUringProvidedBufferRing::checkInvariants() {
+  // This object is carefully packed into two 64 byte cache lines. The first
+  // cache line contains all of the fields accessed during hot code, i.e.
+  // getIoBuf() and returnBuffer(). The second cache line contains all the warm
+  // and cold fields that are rarely accessed.
+  static_assert(
+      sizeof(IoUringProvidedBufferRing) ==
+      2 * folly::hardware_constructive_interference_size);
 
-  ringMemSize_ = align_ceil(ringMemSize_, kBufferAlignBytes);
+  static_assert(
+      alignof(IoUringProvidedBufferRing) ==
+      folly::hardware_constructive_interference_size);
 
-  if (bufferShift_ < 5) {
-    bufferShift_ = 5; // for alignment
+  static_assert(
+      sizeof(folly::DistributedMutex) == 8,
+      "folly::DistributedMutex size changed from 8 bytes");
+}
+
+IoUringProvidedBufferRing::UniquePtr IoUringProvidedBufferRing::create(
+    io_uring* ioRingPtr, Options options) {
+  return IoUringProvidedBufferRing::UniquePtr(
+      new IoUringProvidedBufferRing(ioRingPtr, options));
+}
+
+void IoUringProvidedBufferRing::mapMemory(bool useHugePages) {
+  // Find next power of 2 size larger than bufferCount for the provided buffer
+  // ring count.
+  int ringShift = folly::findLastSet(bufferCount_) - 1;
+  if (bufferCount_ != (1ULL << ringShift)) {
+    ringShift++;
   }
+  ringCount_ = 1U << std::max<int>(ringShift, 1);
+  ringMask_ = ringCount_ - 1;
+  ringMemSize_ = sizeof(struct io_uring_buf) * ringCount_;
+  ringMemSize_ =
+      folly::to_narrow(folly::align_ceil(ringMemSize_, kBufferAlignBytes));
 
-  sizePerBuffer_ = calcBufferSize(bufferShift_);
-  bufferSize_ = sizePerBuffer_ * count;
+  bufferSize_ = sizePerBuffer_ * bufferCount_;
   allSize_ = ringMemSize_ + bufferSize_;
 
   int pages;
-  if (huge_pages) {
-    allSize_ = align_ceil(allSize_, kHugePageSizeBytes);
+  if (useHugePages) {
+    allSize_ =
+        folly::to_narrow(folly::align_ceil(allSize_, kHugePageSizeBytes));
     pages = allSize_ / kHugePageSizeBytes;
   } else {
-    allSize_ = align_ceil(allSize_, kPageSizeBytes);
+    allSize_ = folly::to_narrow(folly::align_ceil(allSize_, kPageSizeBytes));
     pages = allSize_ / kPageSizeBytes;
   }
 
@@ -62,19 +92,20 @@
 
   if (buffer_ == MAP_FAILED) {
     auto errnoCopy = errno;
-    throw std::runtime_error(folly::to<std::string>(
-        "unable to allocate pages of size ",
-        allSize_,
-        " pages=",
-        pages,
-        ": ",
-        folly::errnoStr(errnoCopy)));
+    throw std::runtime_error(
+        folly::to<std::string>(
+            "unable to allocate pages of size ",
+            allSize_,
+            " pages=",
+            pages,
+            ": ",
+            folly::errnoStr(errnoCopy)));
   }
 
-  bufferBuffer_ = ((char*)buffer_) + ringMemSize_;
-  ringPtr_ = (struct io_uring_buf_ring*)buffer_;
+  ringPtr_ = static_cast<struct io_uring_buf_ring*>(buffer_);
+  bufferBuffer_ = static_cast<char*>(buffer_) + ringMemSize_;
 
-  if (huge_pages) {
+  if (useHugePages) {
     int ret = ::madvise(buffer_, allSize_, MADV_HUGEPAGE);
     PLOG_IF(ERROR, ret) << "cannot enable huge pages";
   } else {
@@ -84,29 +115,30 @@
 
 IoUringProvidedBufferRing::IoUringProvidedBufferRing(
     io_uring* ioRingPtr, Options options)
-    : IoUringBufferProviderBase(
-          options.gid,
-          ProvidedBuffersBuffer::calcBufferSize(options.bufferShift)),
+    : bufferStates_(),
+      sizePerBuffer_(std::max(options.bufferSize, kMinBufferSize)),
+      bufferCount_(options.bufferCount),
+      useIncremental_(options.useIncrementalBuffers),
       ioRingPtr_(ioRingPtr),
-      buffer_(
-          options.count,
-          options.bufferShift,
-          options.ringSizeShift,
-          options.useHugePages) {
-  if (options.count > std::numeric_limits<uint16_t>::max()) {
-    throw std::runtime_error("too many buffers");
+      gid_(options.gid) {
+  if (bufferCount_ > std::numeric_limits<uint16_t>::max()) {
+    throw std::runtime_error("bufferCount cannot be larger than 65,535");
   }
-  if (options.count == 0) {
-    throw std::runtime_error("not enough buffers");
+  if (bufferCount_ == 0) {
+    throw std::runtime_error("bufferCount cannot be 0");
   }
 
-  ioBufCallbacks_.assign(
-      (options.count + (sizeof(void*) - 1)) / sizeof(void*), this);
-
+  mapMemory(options.useHugePages);
   initialRegister();
 
-  gottenBuffers_ += options.count;
-  for (size_t i = 0; i < options.count; i++) {
+  bufferStates_ = std::make_unique<BufferState[]>(bufferCount_);
+  for (uint16_t i = 0; i < bufferCount_; i++) {
+    bufferStates_[i].bufId = i;
+    bufferStates_[i].parent = this;
+    bufferStates_[i].offset = 0;
+  }
+
+  for (size_t i = 0; i < bufferCount_; i++) {
     returnBuffer(i);
   }
 }
@@ -121,72 +153,161 @@
     // but if we are processing a batch it doesn't really work
     // because we'll likely get an ENOBUF straight after
     enobuf_.store(true, std::memory_order_relaxed);
+    enobufCount_.fetch_add(1, std::memory_order_relaxed);
   }
-  VLOG_EVERY_N(1, 500) << "enobuf";
 }
 
-void IoUringProvidedBufferRing::unusedBuf(uint16_t i) noexcept {
-  gottenBuffers_++;
-  returnBuffer(i);
+uint32_t IoUringProvidedBufferRing::getAndResetEnobufCount() noexcept {
+  return enobufCount_.exchange(0, std::memory_order_relaxed);
 }
 
 void IoUringProvidedBufferRing::destroy() noexcept {
+  std::unique_lock lock{mutex_};
   ::io_uring_unregister_buf_ring(ioRingPtr_, gid());
-  shutdownReferences_ = 1;
-  auto returned = returnedBuffers_.load();
-  {
-    std::lock_guard guard(shutdownMutex_);
-    wantsShutdown_ = true;
-    // add references for every missing one
-    // we can assume that there will be no more from the kernel side.
-    // there is a race condition here between reading wantsShutdown_ and
-    // a return incrementing the number of returned references, but it is very
-    // unlikely to trigger as everything is shutting down, so there should not
-    // actually be any buffer returns. Worse case this will leak, but since
-    // everything is shutting down anyway it should not be a problem.
-    uint64_t const gotten = gottenBuffers_;
-    DCHECK(gottenBuffers_ >= returned);
-    uint32_t outstanding = (gotten - returned);
-    shutdownReferences_ += outstanding;
+  DCHECK(gottenBuffers_ >= returnedBuffers_);
+  auto remaining = gottenBuffers_ - returnedBuffers_;
+  shutdownReferences_ = remaining;
+  wantsShutdown_ = true;
+  lock.unlock();
+  delayedDestroy(remaining);
+}
+
+void IoUringProvidedBufferRing::returnBuffer(uint16_t i) noexcept {
+  std::unique_lock lock{mutex_};
+  if (FOLLY_UNLIKELY(wantsShutdown_)) {
+    auto refs = --shutdownReferences_;
+    lock.unlock();
+    delayedDestroy(refs);
+    return;
   }
-  if (shutdownReferences_.fetch_sub(1) == 1) {
-    delete this;
+
+  if (useIncremental_) {
+    bufferStates_[i].offset = 0;
+    bufferStates_[i].refCount.store(1);
   }
+
+  uint16_t this_idx = static_cast<uint16_t>(ringReturnedBuffers_++);
+  uint16_t next_tail = this_idx + 1;
+
+  auto* r = ringBuf(this_idx);
+  r->addr = reinterpret_cast<__u64>(getData(i));
+  r->len = sizePerBuffer_;
+  r->bid = i;
+
+  if (tryPublish(this_idx, next_tail)) {
+    enobuf_.store(false, std::memory_order_relaxed);
+  }
 }
 
-std::unique_ptr<IOBuf> IoUringProvidedBufferRing::getIoBuf(
-    uint16_t i, size_t length) noexcept {
+std::unique_ptr<IOBuf> IoUringProvidedBufferRing::getIoBufSingle(
+    uint16_t i, size_t length, bool hasMore) noexcept {
   std::unique_ptr<IOBuf> ret;
   DCHECK(!wantsShutdown_);
+  DCHECK_LT(i, bufferCount_)
+      << "Buffer index " << i << " exceeds buffer count " << bufferCount_;
 
-  // use a weird convention: userData = ioBufCallbacks_.data() + i
-  // ioBufCallbacks_ is just a list of the same pointer, to this
-  // so we don't need to malloc anything
   auto free_fn = [](void*, void* userData) {
-    size_t pprov = (size_t)userData & ~((size_t)(sizeof(void*) - 1));
-    IoUringProvidedBufferRing* prov = *(IoUringProvidedBufferRing**)pprov;
-    uint16_t idx = (size_t)userData - (size_t)prov->ioBufCallbacks_.data();
-    prov->returnBuffer(idx);
+    auto* bufferState = static_cast<BufferState*>(userData);
+    IoUringProvidedBufferRing* parent = bufferState->parent;
+    uint16_t bufId = bufferState->bufId;
+    parent->decBufferState(bufId);
   };
 
-  ret = IOBuf::takeOwnership(
-      (void*)getData(i),
-      sizePerBuffer_,
-      length,
-      free_fn,
-      (void*)(((size_t)ioBufCallbacks_.data()) + i));
-  gottenBuffers_++;
+  if (useIncremental_) {
+    auto* bufferStart = getData(i);
+    unsigned int currentOffset = bufferStates_[i].offset;
+    auto* dataPtr = bufferStart + currentOffset;
+    BufferState* info = &bufferStates_[i];
+    ret = IOBuf::takeOwnership(
+        static_cast<void*>(dataPtr), length, length, free_fn, info);
+  } else {
+    BufferState* info = &bufferStates_[i];
+    ret = IOBuf::takeOwnership(
+        static_cast<void*>(getData(i)), sizePerBuffer_, length, free_fn, info);
+  }
+
+  ret->markExternallySharedOne();
+  incBufferState(i, hasMore, length);
+
   return ret;
 }
 
+std::unique_ptr<IOBuf> IoUringProvidedBufferRing::getIoBuf(
+    uint16_t startBufId, size_t totalLength, bool hasMore) noexcept {
+  if (totalLength <= sizePerBuffer_) {
+    return getIoBufSingle(startBufId, totalLength, hasMore);
+  }
+
+  auto free_fn = [](void*, void* userData) {
+    auto* bufferState = static_cast<BufferState*>(userData);
+    IoUringProvidedBufferRing* parent = bufferState->parent;
+    uint16_t bufId = bufferState->bufId;
+    parent->decBufferState(bufId);
+  };
+
+  std::unique_ptr<IOBuf> head;
+  size_t remainingLength = totalLength;
+  uint16_t currentBufId = startBufId;
+
+  while (remainingLength > 0) {
+    DCHECK_LT(currentBufId, bufferCount_)
+        << "Buffer index " << currentBufId << " exceeds buffer count "
+        << bufferCount_;
+
+    BufferState* bufferState = &bufferStates_[currentBufId];
+    char* bufferStart = getData(currentBufId);
+    unsigned int currentOffset = 0;
+    size_t availableInBuffer = sizePerBuffer_;
+
+    if (useIncremental_) {
+      currentOffset = bufferState->offset;
+      availableInBuffer = sizePerBuffer_ - currentOffset;
+    }
+
+    char* dataPtr = bufferStart + currentOffset;
+    size_t currentChunkSize = std::min(remainingLength, availableInBuffer);
+    bool isLastChunk = (remainingLength <= availableInBuffer);
+
+    std::unique_ptr<IOBuf> chunk;
+    chunk = IOBuf::takeOwnership(
+        static_cast<void*>(dataPtr),
+        useIncremental_ ? currentChunkSize : sizePerBuffer_,
+        currentChunkSize,
+        free_fn,
+        bufferState);
+
+    chunk->markExternallySharedOne();
+    if (!head) {
+      head = std::move(chunk);
+    } else {
+      head->appendToChain(std::move(chunk));
+    }
+
+    incBufferState(currentBufId, hasMore && isLastChunk, currentChunkSize);
+    remainingLength -= currentChunkSize;
+    currentBufId = (currentBufId + 1) & (bufferCount_ - 1);
+  }
+
+  return head;
+}
+
+std::unique_ptr<IOBuf> IoUringProvidedBufferRing::getIoBuf(
+    const struct io_uring_cqe* cqe) noexcept {
+  auto bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
+  auto res = cqe->res;
+  bool hasMore = (cqe->flags & IORING_CQE_F_BUF_MORE) != 0;
+  return getIoBuf(bid, res, hasMore);
+}
+
 void IoUringProvidedBufferRing::initialRegister() {
-  struct io_uring_buf_reg reg;
+  struct io_uring_buf_reg reg{};
   memset(&reg, 0, sizeof(reg));
-  reg.ring_addr = (__u64)buffer_.ring();
-  reg.ring_entries = buffer_.ringCount();
-  reg.bgid = gid();
+  reg.ring_addr = reinterpret_cast<__u64>(ringPtr_);
+  reg.ring_entries = ringCount_;
+  reg.bgid = gid_;
 
-  int ret = ::io_uring_register_buf_ring(ioRingPtr_, &reg, 0);
+  int flags = useIncremental_ ? IOU_PBUF_RING_INC : 0;
+  int ret = ::io_uring_register_buf_ring(ioRingPtr_, &reg, flags);
 
   if (ret) {
     LOG(ERROR) << folly::to<std::string>(
@@ -196,41 +317,65 @@
         folly::errnoStr(-ret));
     LOG(ERROR) << folly::to<std::string>(
         "buffer ring buffer count: ",
-        buffer_.bufferCount(),
+        bufferCount_,
         ", ring count: ",
-        buffer_.ringCount(),
+        ringCount_,
         ", size per buf: ",
-        buffer_.sizePerBuffer(),
+        sizePerBuffer_,
         ", bgid: ",
-        gid());
+        gid_);
     throw LibUringCallError("unable to register provided buffer ring");
   }
 }
 
-void IoUringProvidedBufferRing::returnBufferInShutdown() noexcept {
-  { std::lock_guard guard(shutdownMutex_); }
-  if (shutdownReferences_.fetch_sub(1) == 1) {
+void IoUringProvidedBufferRing::delayedDestroy(uint32_t refs) noexcept {
+  if (refs == 0) {
+    ::munmap(buffer_, allSize_);
     delete this;
   }
 }
 
-void IoUringProvidedBufferRing::returnBuffer(uint16_t i) noexcept {
-  if (FOLLY_UNLIKELY(wantsShutdown_)) {
-    returnBufferInShutdown();
+void IoUringProvidedBufferRing::incBufferState(
+    uint16_t bufId, bool hasMore, size_t bytesConsumed) noexcept {
+  gottenBuffers_++;
+
+  if (useIncremental_ && hasMore) {
+    bufferStates_[bufId].refCount.fetch_add(1);
+    bufferStates_[bufId].offset += bytesConsumed;
+  }
+
+  // No need to handle regular buffers, since it is never really
+  // incremented beyond the original assingment of 1 instead of 0.
+}
+
+void IoUringProvidedBufferRing::decBufferState(uint16_t bufId) noexcept {
+  returnedBuffers_++;
+
+  if (!useIncremental_ || wantsShutdown_) {
+    returnBuffer(bufId);
     return;
   }
-  uint16_t this_idx = static_cast<uint16_t>(returnedBuffers_++);
-  __u64 addr = (__u64)buffer_.buffer(i);
-  uint16_t next_tail = this_idx + 1;
-  auto* r = buffer_.ringBuf(this_idx);
-  r->addr = addr;
-  r->len = buffer_.sizePerBuffer();
-  r->bid = i;
 
-  if (tryPublish(this_idx, next_tail)) {
-    enobuf_.store(false, std::memory_order_relaxed);
+  uint16_t oldRefCount = bufferStates_[bufId].refCount.fetch_sub(1);
+  if (oldRefCount == 1) {
+    returnBuffer(bufId);
   }
-  VLOG(9) << "returnBuffer(" << i << ")@" << this_idx;
+}
+
+int IoUringProvidedBufferRing::getUtilPct() const noexcept {
+  uint32_t totalBuffers = bufferCount_;
+  uint16_t head = 0;
+  int ret = ::io_uring_buf_ring_head(ioRingPtr_, gid(), &head);
+  if (ret != 0) {
+    return ret;
+  }
+  // Use ring mask to extract ring position from wrapped uint16_t counters
+  // Ring size is power of 2, mask handles wrap-around explicitly
+  uint32_t available = (ringPtr_->tail - head) & ringMask_;
+  available = std::min(available, totalBuffers);
+
+  uint32_t inUse = totalBuffers - available;
+  return (100 * inUse) / totalBuffers;
 }
 
 } // namespace folly
diff --git a/folly/folly/io/async/IoUringProvidedBufferRing.h b/folly/folly/io/async/IoUringProvidedBufferRing.h
--- a/folly/folly/io/async/IoUringProvidedBufferRing.h
+++ b/folly/folly/io/async/IoUringProvidedBufferRing.h
@@ -18,52 +18,91 @@
 
 #include <folly/io/async/IoUringBase.h>
 #include <folly/io/async/Liburing.h>
-#include <folly/portability/SysMman.h>
+#include <folly/synchronization/DistributedMutex.h>
 
 #if FOLLY_HAS_LIBURING
 
 FOLLY_PUSH_WARNING
 FOLLY_CLANG_DISABLE_WARNING("-Wnested-anon-types")
 FOLLY_CLANG_DISABLE_WARNING("-Wzero-length-array")
+FOLLY_GCC_DISABLE_WARNING("-Wshadow")
 #include <liburing.h> // @manual
 FOLLY_POP_WARNING
 
 namespace folly {
 
-class IoUringProvidedBufferRing : public IoUringBufferProviderBase {
+class IoUringProvidedBufferRing {
  public:
+  friend class IoUringProvidedBufferRingTestHelper;
+
   class LibUringCallError : public std::runtime_error {
    public:
     using std::runtime_error::runtime_error;
   };
 
+  struct Deleter {
+    void operator()(IoUringProvidedBufferRing* ring) {
+      if (ring) {
+        ring->destroy();
+      }
+    }
+  };
+
+  using UniquePtr = std::unique_ptr<IoUringProvidedBufferRing, Deleter>;
+
   struct Options {
     uint16_t gid{0};
-    size_t count{0};
-    int bufferShift{0};
-    int ringSizeShift{0};
+    uint32_t bufferCount{0};
+    uint32_t bufferSize{0};
     bool useHugePages{false};
+    bool useIncrementalBuffers{false};
   };
 
-  IoUringProvidedBufferRing(io_uring* ioRingPtr, Options options);
+  static UniquePtr create(io_uring* ioRingPtr, Options options);
 
-  void enobuf() noexcept override;
-  void unusedBuf(uint16_t i) noexcept override;
-  void destroy() noexcept override;
-  std::unique_ptr<IOBuf> getIoBuf(uint16_t i, size_t length) noexcept override;
+  ~IoUringProvidedBufferRing() = default;
 
-  uint32_t count() const noexcept override { return buffer_.bufferCount(); }
-  bool available() const noexcept override {
+  void enobuf() noexcept;
+  uint32_t getAndResetEnobufCount() noexcept;
+  void destroy() noexcept;
+
+  std::unique_ptr<IOBuf> getIoBuf(
+      uint16_t startBufId, size_t totalLength, bool hasMore) noexcept;
+  std::unique_ptr<IOBuf> getIoBuf(const struct io_uring_cqe* cqe) noexcept;
+
+  uint32_t count() const noexcept { return bufferCount_; }
+  bool available() const noexcept {
     return !enobuf_.load(std::memory_order_relaxed);
   }
+  size_t sizePerBuffer() const noexcept { return sizePerBuffer_; }
+  uint16_t gid() const noexcept { return gid_; }
 
+  // Returns the buffer utilization as an integer percentage (0-100).
+  int getUtilPct() const noexcept;
+
  private:
+  explicit IoUringProvidedBufferRing(io_uring* ioRingPtr, Options options);
+
+  IoUringProvidedBufferRing(IoUringProvidedBufferRing&&) = delete;
+  IoUringProvidedBufferRing(IoUringProvidedBufferRing const&) = delete;
+  IoUringProvidedBufferRing& operator=(IoUringProvidedBufferRing&&) = delete;
+  IoUringProvidedBufferRing& operator=(IoUringProvidedBufferRing const&) =
+      delete;
+
+  void mapMemory(bool useHugePages);
   void initialRegister();
-  void returnBufferInShutdown() noexcept;
+
   void returnBuffer(uint16_t i) noexcept;
 
+  void delayedDestroy(uint32_t refs) noexcept;
+  void incBufferState(
+      uint16_t bufId, bool hasMore, size_t bytesConsumed) noexcept;
+  void decBufferState(uint16_t bufId) noexcept;
+  std::unique_ptr<IOBuf> getIoBufSingle(
+      uint16_t i, size_t length, bool hasMore) noexcept;
+
   std::atomic<uint16_t>* sharedTail() {
-    return reinterpret_cast<std::atomic<uint16_t>*>(&buffer_.ring()->tail);
+    return reinterpret_cast<std::atomic<uint16_t>*>(&ringPtr_->tail);
   }
 
   bool tryPublish(uint16_t expected, uint16_t value) noexcept {
@@ -71,64 +110,52 @@
         expected, value, std::memory_order_release);
   }
 
-  char const* getData(uint16_t i) { return buffer_.buffer(i); }
-
-  class ProvidedBuffersBuffer {
-   public:
-    ProvidedBuffersBuffer(
-        size_t count, int bufferShift, int ringCountShift, bool huge_pages);
-    ~ProvidedBuffersBuffer() { ::munmap(buffer_, allSize_); }
-
-    static size_t calcBufferSize(int bufferShift) {
-      return 1LLU << std::max<int>(5, bufferShift);
-    }
-
-    struct io_uring_buf_ring* ring() const noexcept { return ringPtr_; }
-
-    struct io_uring_buf* ringBuf(int idx) const noexcept {
-      return &ringPtr_->bufs[idx & ringMask_];
-    }
-
-    uint32_t bufferCount() const noexcept { return bufferCount_; }
-    uint32_t ringCount() const noexcept { return 1 + ringMask_; }
-
-    char* buffer(uint16_t idx) {
-      size_t offset = (size_t)idx << bufferShift_;
-      return bufferBuffer_ + offset;
-    }
-
-    size_t sizePerBuffer() const { return sizePerBuffer_; }
-
-   private:
-    void* buffer_;
-    size_t allSize_;
-
-    size_t ringMemSize_;
-    struct io_uring_buf_ring* ringPtr_;
-    int ringMask_;
+  char* getData(uint16_t i) {
+    auto offset = static_cast<size_t>(i) * sizePerBuffer_;
+    return bufferBuffer_ + offset;
+  }
 
-    size_t bufferSize_;
-    size_t bufferShift_;
-    size_t sizePerBuffer_;
-    char* bufferBuffer_;
-    uint32_t bufferCount_;
+  struct io_uring_buf* ringBuf(int idx) const noexcept {
+    return &ringPtr_->bufs[idx & ringMask_];
+  }
 
-    static constexpr size_t kHugePageSizeBytes = 1024 * 1024 * 2;
-    static constexpr size_t kPageSizeBytes = 4096;
-    static constexpr size_t kBufferAlignBytes = 32;
+  struct BufferState {
+    uint16_t bufId{0};
+    // Starting with a refCount of 1, to account for moreData incoming
+    // in the incremental buffer case.
+    std::atomic<uint32_t> refCount{1};
+    unsigned int offset{0};
+    IoUringProvidedBufferRing* parent{nullptr};
   };
 
-  io_uring* ioRingPtr_;
-  ProvidedBuffersBuffer buffer_;
-  std::atomic<bool> enobuf_{false};
-  std::vector<IoUringProvidedBufferRing*> ioBufCallbacks_;
-
-  uint64_t gottenBuffers_{0};
-  std::atomic<uint64_t> returnedBuffers_{0};
+  static void checkInvariants();
 
+  // Hot fields
+  alignas(folly::hardware_constructive_interference_size)
+      std::unique_ptr<BufferState[]> bufferStates_;
+  struct io_uring_buf_ring* ringPtr_{nullptr};
+  char* bufferBuffer_{nullptr};
+  folly::DistributedMutex mutex_;
+  uint32_t sizePerBuffer_{0};
+  int ringMask_{0};
+  uint32_t gottenBuffers_{0};
+  uint32_t ringReturnedBuffers_{0};
+  uint32_t returnedBuffers_{0};
+  uint32_t bufferCount_{0};
+  bool useIncremental_{false};
+  std::atomic<bool> enobuf_{false};
   std::atomic<bool> wantsShutdown_{false};
-  std::atomic<uint32_t> shutdownReferences_;
-  std::mutex shutdownMutex_;
+  std::atomic<uint32_t> enobufCount_{0};
+
+  // Cold fields
+  alignas(folly::hardware_constructive_interference_size) io_uring* ioRingPtr_;
+  uint32_t shutdownReferences_{0};
+  uint16_t const gid_{0};
+  uint32_t ringCount_{0};
+  uint32_t allSize_{0};
+  void* buffer_{nullptr};
+  uint32_t ringMemSize_{0};
+  uint32_t bufferSize_{0};
 };
 
 } // namespace folly
diff --git a/folly/folly/io/async/IoUringRecv.cpp b/folly/folly/io/async/IoUringRecv.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/io/async/IoUringRecv.cpp
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/io/async/IoUringRecv.h>
+
+#include <folly/io/async/AsyncSocketException.h>
+#include <folly/io/async/EventBase.h>
+#include <folly/io/async/EventHandler.h>
+#include <folly/io/async/IoUringBackend.h>
+
+namespace folly {
+
+#if FOLLY_HAS_LIBURING
+
+namespace {
+/*
+ * DetachedReadCallback
+ */
+
+class DetachedReadCallback : public IoUringRecvCallback {
+ public:
+  void recvSuccess(std::unique_ptr<IOBuf> data) override {
+    if (!data_) {
+      data_ = std::move(data);
+    } else {
+      data_->appendToChain(std::move(data));
+    }
+  }
+
+  void recvEOF() noexcept override { done(); }
+  void recvErr(
+      int /*err*/,
+      std::unique_ptr<const AsyncSocketException> /*exception*/) noexcept
+      override {
+    done();
+  }
+
+  void done() {
+    promise.setValue(std::move(data_));
+    delete this;
+  }
+
+  Promise<std::unique_ptr<IOBuf>> promise;
+
+ private:
+  std::unique_ptr<IOBuf> data_;
+};
+} // namespace
+
+/*
+ * RecvRequest
+ */
+
+class IoUringRecvHandle::RecvRequest
+    : public IoSqeBase,
+      public DelayedDestruction {
+ public:
+  using UniquePtr = std::unique_ptr<RecvRequest, Destructor>;
+
+  explicit RecvRequest(
+      IoUringBackend* backend,
+      NetworkSocket fd,
+      const SocketAddress& addr,
+      IoUringRecvHandle* handle)
+      : IoSqeBase(IoSqeBase::Type::Read),
+        fd_(fd),
+        handle_(handle),
+        handleGuard_(handle) {
+    if (backend->zcBufferPool() && !addr.isLoopbackAddress()) {
+      bufferPool_ = backend->zcBufferPool();
+    } else {
+      bufferRing_ = backend->bufferProvider();
+    }
+  }
+
+  void setRecvLen(size_t len) { recvLen_ = len; }
+
+  void prepRecvNormal(struct io_uring_sqe* sqe) {
+    if (recvLen_ > 0) {
+      ::io_uring_prep_recv(sqe, fd_.toFd(), nullptr, recvLen_, 0);
+    } else {
+      ::io_uring_prep_recv_multishot(sqe, fd_.toFd(), nullptr, 0, 0);
+    }
+    sqe->buf_group = bufferRing_->gid();
+    sqe->flags |= IOSQE_BUFFER_SELECT;
+  }
+
+  void prepRecvFallback(struct io_uring_sqe* sqe) {
+    size_t size = recvLen_ > 0 ? recvLen_ : goodMallocSize(16384);
+    fallbackBuffer_ = IOBuf::create(size);
+    ::io_uring_prep_recv(
+        sqe,
+        fd_.toFd(),
+        fallbackBuffer_->writableTail(),
+        fallbackBuffer_->tailroom(),
+        0);
+  }
+
+  /*
+   * IoSqeBase
+   */
+  void processSubmit(struct io_uring_sqe* sqe) noexcept override {
+    fallbackBuffer_.reset();
+
+    if (bufferPool_) {
+      ::io_uring_prep_rw(
+          IORING_OP_RECV_ZC,
+          sqe,
+          fd_.toFd(),
+          nullptr,
+          static_cast<uint32_t>(recvLen_),
+          0);
+      sqe->ioprio |= IORING_RECV_MULTISHOT;
+      return;
+    }
+
+    if (bufferRing_->available()) {
+      prepRecvNormal(sqe);
+    } else {
+      prepRecvFallback(sqe);
+    }
+  }
+
+  void callback(const struct io_uring_cqe* cqe) noexcept override {
+    DestructorGuard dg(this);
+    auto res = cqe->res;
+
+    if (res > 0) {
+      handle_->onRecvComplete(getData(cqe));
+      return;
+    }
+
+    if (res == -ECANCELED) {
+      // ignore
+      return;
+    } else if (res == -ENOBUFS) {
+      bufferRing_->enobuf();
+      handle_->onEnobufs();
+      return;
+    } else if (isEOF(cqe)) {
+      handle_->onRecvEOF();
+    } else {
+      handle_->onRecvErr(-res);
+    }
+  }
+
+  void callbackCancelled(const io_uring_cqe* cqe) noexcept override {
+    DestructorGuard dg(this);
+
+    if (cqe->res > 0) {
+      handle_->onRecvComplete(getData(cqe));
+    }
+
+    if (!(cqe->flags & IORING_CQE_F_MORE)) {
+      handle_->onRecvEOF();
+      destroy();
+    }
+  }
+
+ private:
+  std::unique_ptr<IOBuf> getData(const struct io_uring_cqe* cqe) {
+    if (bufferPool_) {
+      const auto* rcqe = (struct io_uring_zcrx_cqe*)(cqe + 1);
+      return bufferPool_->getIoBuf(cqe, rcqe);
+    }
+
+    if (fallbackBuffer_) {
+      fallbackBuffer_->append(cqe->res);
+      return std::move(fallbackBuffer_);
+    }
+
+    return bufferRing_->getIoBuf(cqe);
+  }
+
+  bool isEOF(const struct io_uring_cqe* cqe) {
+    return cqe->res == 0 && (bufferPool_ ? cqe->flags == 0 : true);
+  }
+
+  NetworkSocket fd_;
+  IoUringRecvHandle* handle_;
+  DestructorGuard handleGuard_;
+
+  IoUringProvidedBufferRing* bufferRing_{nullptr};
+  IoUringZeroCopyBufferPool* bufferPool_{nullptr};
+  size_t recvLen_{0};
+  std::unique_ptr<IOBuf> fallbackBuffer_;
+};
+
+/*
+ * IoUringRecvHandle
+ */
+
+IoUringRecvHandle::UniquePtr IoUringRecvHandle::create(
+    EventBase* evb,
+    NetworkSocket fd,
+    const SocketAddress& addr,
+    IoUringRecvCallback* callback) {
+  auto* backend = dynamic_cast<IoUringBackend*>(evb->getBackend());
+  if (!backend) {
+    return nullptr;
+  }
+
+  if (!backend->zcBufferPool() && !backend->hasBufferProvider()) {
+    return nullptr;
+  }
+
+  return UniquePtr(new IoUringRecvHandle(backend, fd, addr, callback));
+}
+
+IoUringRecvHandle::UniquePtr IoUringRecvHandle::clone(
+    EventBase* evb,
+    NetworkSocket fd,
+    const SocketAddress& addr,
+    IoUringRecvCallback* callback,
+    UniquePtr other) {
+  auto* backend = dynamic_cast<IoUringBackend*>(evb->getBackend());
+  if (!backend) {
+    return nullptr;
+  }
+
+  if (!backend->zcBufferPool() && !backend->hasBufferProvider()) {
+    return nullptr;
+  }
+
+  return UniquePtr(new IoUringRecvHandle(
+      evb, backend, fd, addr, callback, std::move(other)));
+}
+
+IoUringRecvHandle::IoUringRecvHandle(
+    IoUringBackend* backend,
+    NetworkSocket fd,
+    const SocketAddress& addr,
+    IoUringRecvCallback* callback)
+    : backend_(backend),
+      recvCallback_(callback),
+      request_(
+          RecvRequest::UniquePtr(new RecvRequest(backend, fd, addr, this))) {}
+
+IoUringRecvHandle::IoUringRecvHandle(
+    EventBase* evb,
+    IoUringBackend* backend,
+    NetworkSocket fd,
+    const SocketAddress& addr,
+    IoUringRecvCallback* callback,
+    UniquePtr other)
+    : backend_(backend),
+      recvCallback_(callback),
+      request_(
+          RecvRequest::UniquePtr(new RecvRequest(backend, fd, addr, this))),
+      queuedReceivedData_(std::move(other->queuedReceivedData_)) {
+  if (other->pendingRead_.has_value()) {
+    setPendingRead(evb, std::move(*other->pendingRead_));
+  }
+}
+
+bool IoUringRecvHandle::update(uint16_t eventFlags) {
+  if (!readEnabled_ && eventFlags & EventHandler::READ) {
+    CHECK(!readEnabled_);
+    readEnabled_ = true;
+  } else if (readEnabled_ && !(eventFlags & EventHandler::READ)) {
+    CHECK(readEnabled_);
+    readEnabled_ = false;
+  }
+
+  if (pendingRead_) {
+    processPendingRead();
+  }
+
+  return true;
+}
+
+void IoUringRecvHandle::submit(size_t maxSize) {
+  CHECK(readEnabled_);
+  CHECK(request_);
+  // Some ReadCallbacks have a small peeking getReadBuffer() size, intended to
+  // peek a few bytes in the socket. For these, issue a non-multishot recv.
+  request_->setRecvLen(maxSize < kSmallRecvSize ? maxSize : 0);
+  if (pendingRead_) {
+    if (!pendingRead_->isReady()) {
+      return;
+    }
+    processPendingRead();
+  }
+
+  if (!request_->inFlight()) {
+    backend_->submitSoon(*request_);
+  }
+}
+
+bool IoUringRecvHandle::hasQueuedData() {
+  return queuedReceivedData_ && !queuedReceivedData_->empty();
+}
+
+std::unique_ptr<IOBuf> IoUringRecvHandle::getQueuedData() {
+  return std::move(queuedReceivedData_);
+}
+
+void IoUringRecvHandle::detachEventBase() {
+  CHECK_NE(backend_, nullptr);
+
+  if (request_->inFlight()) {
+    auto* drc = new DetachedReadCallback();
+    auto thisPendingRead = drc->promise.getSemiFuture();
+
+    if (pendingRead_) {
+      pendingRead_ =
+          std::move(*pendingRead_)
+              .deferValue([currRead = std::move(thisPendingRead)](
+                              std::unique_ptr<IOBuf>&& prevData) mutable {
+                return std::move(currRead).deferValue(
+                    [data = std::move(prevData)](
+                        std::unique_ptr<IOBuf>&& currData) mutable {
+                      if (!data) {
+                        return std::move(currData);
+                      }
+                      if (currData) {
+                        data->appendToChain(std::move(currData));
+                      }
+                      return std::move(data);
+                    });
+              });
+    } else {
+      pendingRead_ = std::move(thisPendingRead);
+    }
+
+    backend_->cancel(request_.release());
+    recvCallback_ = drc;
+  } else {
+    request_.reset();
+    recvCallback_ = nullptr;
+  }
+
+  backend_ = nullptr;
+}
+
+void IoUringRecvHandle::cancel() {
+  if (request_->inFlight()) {
+    request_->setEventBase(nullptr);
+    backend_->cancel(request_.release());
+  } else {
+    request_.reset();
+  }
+  recvCallback_ = nullptr;
+  backend_ = nullptr;
+  return;
+}
+
+void IoUringRecvHandle::setPendingRead(EventBase* evb, PendingRead&& prevRead) {
+  pendingRead_ = std::move(prevRead).via(evb).thenValue(
+      [this, evb, dg = DestructorGuard(this)](auto&& prevData) {
+        if (backend_) {
+          evb->add([this, dg = DestructorGuard(this)] {
+            processPendingRead();
+          });
+        }
+        return std::move(prevData);
+      });
+}
+
+void IoUringRecvHandle::processPendingRead() {
+  if (!pendingRead_.has_value() || !backend_) {
+    return;
+  }
+
+  DestructorGuard dg(this);
+  if (pendingRead_->isReady()) {
+    auto data = std::move(*pendingRead_).get();
+    pendingRead_.reset();
+
+    if (!queuedReceivedData_) {
+      queuedReceivedData_ = std::move(data);
+    } else {
+      queuedReceivedData_->appendToChain(std::move(data));
+    }
+
+    if (readEnabled_) {
+      recvCallback_->recvSuccess(std::move(queuedReceivedData_));
+    }
+
+    if (backend_ && readEnabled_ && !request_->inFlight()) {
+      backend_->submitSoon(*request_);
+    }
+  }
+}
+
+void IoUringRecvHandle::onRecvComplete(std::unique_ptr<IOBuf> data) {
+  DestructorGuard dg(this);
+  if (backend_ == nullptr) {
+    CHECK(!request_);
+    if (recvCallback_) {
+      // DetachedReadCallback
+      recvCallback_->recvSuccess(std::move(data));
+    }
+    return;
+  }
+
+  if (readEnabled_) {
+    CHECK(!queuedReceivedData_);
+    recvCallback_->recvSuccess(std::move(data));
+  } else {
+    if (!queuedReceivedData_) {
+      queuedReceivedData_ = std::move(data);
+    } else {
+      queuedReceivedData_->appendToChain(std::move(data));
+    }
+  }
+
+  if (backend_ && readEnabled_ && !request_->inFlight()) {
+    backend_->submitSoon(*request_);
+  }
+}
+
+void IoUringRecvHandle::onEnobufs() {
+  CHECK(!request_->inFlight());
+  if (backend_ && readEnabled_) {
+    backend_->submitSoon(*request_);
+  }
+}
+
+void IoUringRecvHandle::onRecvEOF() {
+  DestructorGuard dg(this);
+  if (recvCallback_) {
+    recvCallback_->recvEOF();
+  }
+}
+
+void IoUringRecvHandle::onRecvErr(int err) {
+  DestructorGuard dg(this);
+  if (recvCallback_) {
+    recvCallback_->recvErr(err, nullptr);
+  }
+}
+
+#else
+
+class IoUringRecvHandle::RecvRequest : public DelayedDestruction {};
+
+IoUringRecvHandle::UniquePtr IoUringRecvHandle::create(
+    EventBase* /*evb*/,
+    NetworkSocket /*fd*/,
+    const SocketAddress& /*addr*/,
+    IoUringRecvCallback* /*callback*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+IoUringRecvHandle::UniquePtr IoUringRecvHandle::clone(
+    EventBase* /*evb*/,
+    NetworkSocket /*fd*/,
+    const SocketAddress& /*addr*/,
+    IoUringRecvCallback* /*callback*/,
+    IoUringRecvHandle::UniquePtr /*old*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+IoUringRecvHandle::IoUringRecvHandle(
+    IoUringBackend* /*backend*/,
+    NetworkSocket /*fd*/,
+    const SocketAddress& /*addr*/,
+    IoUringRecvCallback* /*callback*/) {
+  (void)backend_;
+  (void)recvCallback_;
+  (void)request_;
+  (void)queuedReceivedData_;
+  (void)readEnabled_;
+  (void)pendingRead_;
+}
+
+IoUringRecvHandle::IoUringRecvHandle(
+    EventBase* /*evb*/,
+    IoUringBackend* /*backend*/,
+    NetworkSocket /*fd*/,
+    const SocketAddress& /*addr*/,
+    IoUringRecvCallback* /*callback*/,
+    UniquePtr /*other*/) {
+  (void)backend_;
+  (void)recvCallback_;
+  (void)request_;
+  (void)queuedReceivedData_;
+  (void)readEnabled_;
+  (void)pendingRead_;
+}
+
+bool IoUringRecvHandle::update(uint16_t /*eventFlags*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::submit(size_t /*maxSize*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+bool IoUringRecvHandle::hasQueuedData() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+std::unique_ptr<IOBuf> IoUringRecvHandle::getQueuedData() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::detachEventBase() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::cancel() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::setPendingRead(
+    EventBase* /*evb*/, PendingRead&& /*future*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::processPendingRead() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::onRecvComplete(std::unique_ptr<IOBuf> /*data*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::onEnobufs() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::onRecvEOF() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringRecvHandle::onRecvErr(int /*err*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+#endif
+
+} // namespace folly
diff --git a/folly/folly/io/async/IoUringRecv.h b/folly/folly/io/async/IoUringRecv.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/io/async/IoUringRecv.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <folly/SocketAddress.h>
+#include <folly/futures/Future.h>
+#include <folly/io/async/AsyncTransport.h>
+#include <folly/io/async/DelayedDestruction.h>
+#include <folly/io/async/IoUringBase.h>
+#include <folly/net/NetworkSocket.h>
+
+namespace folly {
+
+class EventBase;
+class IoUringBackend;
+
+class IoUringRecvCallback {
+ public:
+  virtual ~IoUringRecvCallback() = default;
+
+  virtual void recvSuccess(std::unique_ptr<IOBuf> data) = 0;
+  virtual void recvEOF() noexcept = 0;
+  virtual void recvErr(
+      int err,
+      std::unique_ptr<const AsyncSocketException> exception) noexcept = 0;
+};
+
+class IoUringRecvHandle : public DelayedDestruction {
+ public:
+  using UniquePtr = std::unique_ptr<IoUringRecvHandle, Destructor>;
+  using PendingRead = SemiFuture<std::unique_ptr<IOBuf>>;
+  using PendingReadMaybe = Optional<PendingRead>;
+
+  static constexpr size_t kSmallRecvSize = 4096;
+
+  static UniquePtr create(
+      EventBase* evb,
+      NetworkSocket fd,
+      const SocketAddress& addr,
+      IoUringRecvCallback* callback);
+
+  static UniquePtr clone(
+      EventBase* evb,
+      NetworkSocket fd,
+      const SocketAddress& addr,
+      IoUringRecvCallback* callback,
+      UniquePtr old);
+
+  bool update(uint16_t eventFlags);
+  void submit(size_t maxSize);
+
+  bool hasQueuedData();
+  std::unique_ptr<IOBuf> getQueuedData();
+
+  void detachEventBase();
+  void cancel();
+
+ private:
+  explicit IoUringRecvHandle(
+      IoUringBackend* backend,
+      NetworkSocket fd,
+      const SocketAddress& addr,
+      IoUringRecvCallback* callback);
+
+  explicit IoUringRecvHandle(
+      EventBase* evb,
+      IoUringBackend* backend,
+      NetworkSocket fd,
+      const SocketAddress& addr,
+      IoUringRecvCallback* callback,
+      UniquePtr other);
+
+  void setPendingRead(EventBase* evb, PendingRead&& future);
+  void processPendingRead();
+
+  class RecvRequest;
+  void onRecvComplete(std::unique_ptr<IOBuf> data);
+  void onEnobufs();
+  void onRecvEOF();
+  void onRecvErr(int err);
+
+  IoUringBackend* backend_;
+  IoUringRecvCallback* recvCallback_;
+  std::unique_ptr<RecvRequest, DelayedDestruction::Destructor> request_;
+
+  std::unique_ptr<IOBuf> queuedReceivedData_;
+  bool readEnabled_{false};
+  PendingReadMaybe pendingRead_{folly::none};
+};
+
+} // namespace folly
diff --git a/folly/folly/io/async/IoUringSend.cpp b/folly/folly/io/async/IoUringSend.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/io/async/IoUringSend.cpp
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/io/async/IoUringSend.h>
+
+#include <folly/io/async/EventHandler.h>
+#include <folly/io/async/IoUringBackend.h>
+
+namespace folly {
+
+#if FOLLY_HAS_LIBURING
+
+/*
+ * SendRequest
+ */
+
+class IoUringSendHandle::SendRequest : public IoSqeBase {
+ public:
+  static void* alloc(size_t iovCount) {
+    size_t bufSize = 0;
+    if (!checked_muladd<size_t>(
+            &bufSize, iovCount, sizeof(struct iovec), sizeof(SendRequest))) {
+      throw std::bad_alloc();
+    }
+    void* buf = malloc(bufSize);
+    if (buf == nullptr) {
+      throw std::bad_alloc();
+    }
+    return buf;
+  }
+
+  explicit SendRequest(
+      AsyncWriter::WriteCallback* callback,
+      const struct iovec* iov,
+      size_t iovCount,
+      size_t partialWritten,
+      size_t bytesWritten,
+      std::unique_ptr<IOBuf> data,
+      WriteFlags flags,
+      NetworkSocket fd)
+      : IoSqeBase(IoSqeBase::Type::Write),
+        callback_(callback),
+        iovRemaining_(iovCount),
+        bytesWritten_(bytesWritten),
+        data_(std::move(data)),
+        flags_(flags),
+        fd_(fd) {
+    if (data_) {
+      CHECK_EQ(data_->countChainElements(), iovCount);
+    }
+    memcpy(iov_, iov, sizeof(struct iovec) * iovCount);
+    msg_.msg_iov = iov_;
+    msg_.msg_iovlen = std::min<size_t>(iovRemaining_, kIovMax);
+
+    iov_->iov_base =
+        reinterpret_cast<uint8_t*>(iov_->iov_base) + partialWritten;
+    iov_->iov_len -= partialWritten;
+  }
+
+  void destroy() {
+    if (!cancelled() && handle_) {
+      handle_->onReleaseIOBuf(
+          std::move(data_), callback_->getReleaseIOBufCallback());
+    }
+    this->~SendRequest();
+    free(this);
+  }
+
+  void setHandle(IoUringSendHandle* handle) {
+    handle_ = handle;
+    handleGuard_ = DestructorGuard(handle);
+  }
+  SendRequest* getNext() { return next_; }
+  void append(SendRequest* request) { next_ = request; }
+  AsyncWriter::WriteCallback* getCallback() { return callback_; }
+  size_t getTotalBytesWritten() { return bytesWritten_; }
+
+  void releaseIOBuf(IoUringSendHandle* handle) {
+    CHECK(!cancelled());
+    handle->onReleaseIOBuf(
+        std::move(data_), callback_->getReleaseIOBufCallback());
+  }
+
+  folly::SemiFuture<int> detachEventBase() {
+    handle_ = nullptr;
+    setEventBase(nullptr);
+
+    auto [promise, future] = makePromiseContract<int>();
+    detachedSignal_ = [p = std::move(promise)](int res) mutable {
+      p.setValue(res);
+    };
+    return std::move(future);
+  }
+
+  SendRequest* clone(IoUringSendHandle* newHandle) {
+    CHECK(handle_ == nullptr);
+    void* buf = alloc(msg_.msg_iovlen);
+    auto clone = new (buf) SendRequest(
+        callback_,
+        msg_.msg_iov,
+        msg_.msg_iovlen,
+        0,
+        bytesWritten_,
+        std::move(data_),
+        flags_,
+        fd_);
+    clone->append(next_);
+    if (inFlight()) {
+      clone->internalMarkInflight(true);
+      clone->setHandle(newHandle);
+    }
+    return clone;
+  }
+
+  /*
+   * IoSqeBase
+   */
+  void processSubmit(struct io_uring_sqe* sqe) noexcept override {
+    ::io_uring_prep_sendmsg(sqe, fd_.toFd(), &msg_, flags());
+    handle_->onSendStarted();
+  }
+
+  void callback(const struct io_uring_cqe* cqe) noexcept override {
+    auto res = cqe->res;
+
+    if (!handle_) {
+      detachedSignal_(res);
+      return;
+    }
+
+    if (cancelled()) {
+      return;
+    }
+
+    if (res >= 0) {
+      consumeBytes(res);
+      if (msg_.msg_iovlen > 0) {
+        prepareForReuse();
+        handle_->onSendPartial(res);
+      } else {
+        handle_->onSendComplete(res);
+      }
+    } else {
+      handle_->onSendErr(-res);
+    }
+  }
+
+  void callbackCancelled(const io_uring_cqe*) noexcept override { destroy(); }
+
+ private:
+  int flags() {
+    int msg_flags = MSG_NOSIGNAL;
+
+    if (isSet(flags_, WriteFlags::CORK)) {
+      // MSG_MORE tells the kernel we have more data to send, so wait for us to
+      // give it the rest of the data rather than immediately sending a partial
+      // frame, even when TCP_NODELAY is enabled.
+      msg_flags |= MSG_MORE;
+    }
+
+    if (isSet(flags_, WriteFlags::EOR)) {
+      // marks that this is the last byte of a record (response)
+      msg_flags |= MSG_EOR;
+    }
+
+    return msg_flags;
+  }
+
+  void consumeBytes(size_t bytes) {
+    bytesWritten_ += bytes;
+
+    while (bytes > 0 && iovRemaining_ > 0) {
+      if (msg_.msg_iov->iov_len > bytes) {
+        msg_.msg_iov->iov_base =
+            static_cast<char*>(msg_.msg_iov->iov_base) + bytes;
+        msg_.msg_iov->iov_len -= bytes;
+        break;
+      }
+
+      bytes -= msg_.msg_iov->iov_len;
+      ++msg_.msg_iov;
+      --iovRemaining_;
+
+      // There is a 1:1 relationship between IOBufs and iovecs.
+      if (data_) {
+        auto next = data_->pop();
+        handle_->onReleaseIOBuf(
+            std::move(data_), callback_->getReleaseIOBufCallback());
+        data_ = std::move(next);
+      }
+    }
+
+    msg_.msg_iovlen = std::min<size_t>(iovRemaining_, kIovMax);
+  }
+
+  AsyncWriter::WriteCallback* callback_;
+  size_t iovRemaining_;
+  size_t bytesWritten_;
+  std::unique_ptr<IOBuf> data_;
+  WriteFlags flags_;
+  NetworkSocket fd_;
+
+  IoUringSendHandle* handle_{nullptr};
+  DestructorGuard handleGuard_{nullptr};
+  SendRequest* next_{nullptr};
+  struct msghdr msg_{};
+  folly::Function<void(int)> detachedSignal_;
+
+  struct iovec iov_[];
+};
+
+/*
+ * IoUringSendHandle
+ */
+
+IoUringSendHandle::UniquePtr IoUringSendHandle::create(
+    EventBase* evb,
+    NetworkSocket fd,
+    const folly::SocketAddress& addr,
+    IoUringSendCallback* callback) {
+  auto* backend = dynamic_cast<IoUringBackend*>(evb->getBackend());
+  if (!backend) {
+    return nullptr;
+  }
+
+  return UniquePtr(new IoUringSendHandle(evb, backend, fd, addr, callback));
+}
+
+IoUringSendHandle::UniquePtr IoUringSendHandle::clone(
+    EventBase* evb, IoUringSendHandle::UniquePtr other) {
+  auto* backend = dynamic_cast<IoUringBackend*>(evb->getBackend());
+  if (!backend) {
+    return nullptr;
+  }
+
+  return UniquePtr(new IoUringSendHandle(evb, backend, std::move(other)));
+}
+
+IoUringSendHandle::IoUringSendHandle(
+    EventBase* evb,
+    IoUringBackend* backend,
+    NetworkSocket fd,
+    const folly::SocketAddress& addr,
+    IoUringSendCallback* callback)
+    : evb_(evb),
+      backend_(backend),
+      fd_(fd),
+      addr_(addr),
+      sendCallback_(callback) {}
+
+IoUringSendHandle::IoUringSendHandle(
+    EventBase* evb, IoUringBackend* backend, IoUringSendHandle::UniquePtr other)
+    : evb_(evb),
+      backend_(backend),
+      fd_(other->fd_),
+      addr_(other->addr_),
+      sendCallback_(other->sendCallback_) {
+  if (!other->empty()) {
+    auto* oldReq = other->requestHead_;
+    auto* newReq = oldReq->clone(this);
+    requestHead_ = newReq;
+    requestTail_ = newReq->getNext() == nullptr ? newReq : other->requestTail_;
+
+    if (other->detachedFuture_.has_value()) {
+      CHECK(oldReq->inFlight());
+      CHECK(newReq->inFlight());
+      std::move(*other->detachedFuture_)
+          .via(evb)
+          .thenValue([oldReq, newReq, evb](int res) {
+            // The result res is from detachSignal_ in the previous request
+            oldReq->destroy();
+            struct io_uring_cqe cqe{};
+            cqe.res = res;
+            evb->bumpHandlingTime();
+            if (newReq->cancelled()) {
+              newReq->callbackCancelled(&cqe);
+            } else {
+              newReq->callback(&cqe);
+            }
+          });
+    }
+  }
+}
+
+void IoUringSendHandle::detachEventBase() {
+  CHECK(!sendEnabled_);
+  evb_ = nullptr;
+  backend_ = nullptr;
+
+  if (requestHead_ && requestHead_->inFlight()) {
+    detachedFuture_ = requestHead_->detachEventBase();
+  }
+}
+
+bool IoUringSendHandle::update(uint16_t eventFlags) {
+  if (!sendEnabled_ && (eventFlags & EventHandler::WRITE)) {
+    sendEnabled_ = true;
+    trySubmit();
+  } else if (sendEnabled_ && !(eventFlags & EventHandler::WRITE)) {
+    sendEnabled_ = false;
+  }
+
+  return true;
+}
+
+void IoUringSendHandle::write(
+    AsyncWriter::WriteCallback* callback,
+    const struct iovec* iov,
+    size_t iovCount,
+    size_t partialWritten,
+    size_t bytesWritten,
+    std::unique_ptr<IOBuf> data,
+    WriteFlags flags) {
+  CHECK_GT(iovCount, 0);
+  void* buf = SendRequest::alloc(iovCount);
+  auto req = new (buf) SendRequest(
+      callback,
+      iov,
+      iovCount,
+      partialWritten,
+      bytesWritten,
+      std::move(data),
+      flags,
+      fd_);
+  req->setEventBase(evb_);
+
+  if (requestTail_ == nullptr) {
+    CHECK(requestHead_ == nullptr);
+    requestHead_ = requestTail_ = req;
+    trySubmit();
+  } else {
+    requestTail_->append(req);
+    requestTail_ = req;
+  }
+}
+
+void IoUringSendHandle::failWrite(const AsyncSocketException& ex) {
+  if (requestHead_ != nullptr) {
+    auto req = requestHead_;
+    requestHead_ = req->getNext();
+    auto* callback = req->getCallback();
+    auto bytesWritten = req->getTotalBytesWritten();
+
+    req->releaseIOBuf(this);
+    if (req->inFlight()) {
+      backend_->cancel(req);
+    } else {
+      req->destroy();
+    }
+
+    if (callback) {
+      callback->writeErr(bytesWritten, ex);
+    }
+  }
+}
+
+void IoUringSendHandle::failAllWrites(const AsyncSocketException& ex) {
+  while (requestHead_ != nullptr) {
+    failWrite(ex);
+  }
+}
+
+void IoUringSendHandle::trySubmit() {
+  if (sendEnabled_ && requestHead_ && !requestHead_->inFlight()) {
+    requestHead_->setHandle(this);
+    backend_->submitSoon(*requestHead_);
+  }
+}
+
+void IoUringSendHandle::onSendStarted() {
+  requestHead_->getCallback()->writeStarting();
+}
+
+void IoUringSendHandle::onSendPartial(size_t bytesWritten) {
+  CHECK(requestHead_ != nullptr);
+  sendCallback_->sendPartial(bytesWritten);
+  trySubmit();
+}
+
+void IoUringSendHandle::onSendComplete(size_t bytesWritten) {
+  DestructorGuard dg(this);
+  CHECK(requestHead_ != nullptr);
+  auto req = requestHead_;
+  requestHead_ = req->getNext();
+  if (requestHead_ == nullptr) {
+    requestTail_ = nullptr;
+    sendCallback_->sendDone(bytesWritten);
+    // sets sendEnabled_ to false
+  }
+
+  auto* callback = req->getCallback();
+  req->destroy();
+  if (callback) {
+    callback->writeSuccess();
+  }
+
+  trySubmit();
+}
+
+void IoUringSendHandle::onSendErr(int err) {
+  sendCallback_->sendErr(err);
+}
+
+void IoUringSendHandle::onReleaseIOBuf(
+    std::unique_ptr<IOBuf> data, AsyncWriter::ReleaseIOBufCallback* callback) {
+  sendCallback_->releaseIOBuf(std::move(data), callback);
+}
+
+#else
+
+IoUringSendHandle::UniquePtr IoUringSendHandle::create(
+    EventBase* /*evb*/,
+    NetworkSocket /*fd*/,
+    const folly::SocketAddress& /*addr*/,
+    IoUringSendCallback* /*callback*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+IoUringSendHandle::IoUringSendHandle(
+    EventBase* /*evb*/,
+    IoUringBackend* /*backend*/,
+    NetworkSocket /*fd*/,
+    const folly::SocketAddress& /*addr*/,
+    IoUringSendCallback* /*callback*/) {
+  (void)evb_;
+  (void)backend_;
+  (void)fd_;
+  (void)addr_;
+  (void)sendCallback_;
+  (void)requestHead_;
+  (void)requestTail_;
+  (void)sendEnabled_;
+  (void)detachedFuture_;
+}
+
+IoUringSendHandle::UniquePtr IoUringSendHandle::clone(
+    EventBase* /*evb*/, IoUringSendHandle::UniquePtr /*other*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+IoUringSendHandle::IoUringSendHandle(
+    EventBase* /*evb*/,
+    IoUringBackend* /*backend*/,
+    IoUringSendHandle::UniquePtr /*other*/) {
+  (void)evb_;
+  (void)backend_;
+  (void)fd_;
+  (void)addr_;
+  (void)sendCallback_;
+  (void)requestHead_;
+  (void)requestTail_;
+  (void)sendEnabled_;
+  (void)detachedFuture_;
+}
+
+void IoUringSendHandle::detachEventBase() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+bool IoUringSendHandle::update(uint16_t /*eventFlags*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::write(
+    AsyncWriter::WriteCallback* /*callback*/,
+    const struct iovec* /*iov*/,
+    size_t /*iovCount*/,
+    size_t /*partialWritten*/,
+    size_t /*bytesWritten*/,
+    std::unique_ptr<IOBuf> /*data*/,
+    WriteFlags /*flags*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::failWrite(const AsyncSocketException& /*ex*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::failAllWrites(const AsyncSocketException& /*ex*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::trySubmit() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::onSendStarted() {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::onSendPartial(size_t /*bytesWritten*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::onSendComplete(size_t /*bytesWritten*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::onSendErr(int /*err*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+void IoUringSendHandle::onReleaseIOBuf(
+    std::unique_ptr<IOBuf> /*data*/,
+    AsyncWriter::ReleaseIOBufCallback* /*callback*/) {
+  folly::terminate_with<std::runtime_error>("io_uring not supported");
+}
+
+#endif
+
+} // namespace folly
diff --git a/folly/folly/io/async/IoUringSend.h b/folly/folly/io/async/IoUringSend.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/io/async/IoUringSend.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/futures/Future.h>
+#include <folly/io/async/AsyncTransport.h>
+#include <folly/io/async/DelayedDestruction.h>
+#include <folly/io/async/IoUringBase.h>
+#include <folly/net/NetworkSocket.h>
+
+namespace folly {
+
+class IoUringSendCallback {
+ public:
+  virtual ~IoUringSendCallback() = default;
+
+  virtual void sendPartial(size_t bytesWritten) = 0;
+  virtual void sendDone(size_t bytesWritten) = 0;
+  virtual void sendErr(int err) = 0;
+  virtual void releaseIOBuf(
+      std::unique_ptr<IOBuf> buf,
+      AsyncWriter::ReleaseIOBufCallback* callback) = 0;
+};
+
+class EventBase;
+class IoUringBackend;
+
+class IoUringSendHandle : public DelayedDestruction {
+ public:
+  using UniquePtr = std::unique_ptr<IoUringSendHandle, Destructor>;
+
+  static UniquePtr create(
+      EventBase* evb,
+      NetworkSocket fd,
+      const SocketAddress& addr,
+      IoUringSendCallback* callback);
+
+  static UniquePtr clone(EventBase* evb, IoUringSendHandle::UniquePtr other);
+  void detachEventBase();
+
+  bool update(uint16_t eventFlags);
+  void write(
+      AsyncWriter::WriteCallback* callback,
+      const struct iovec* iov,
+      size_t iovCount,
+      size_t partialWritten,
+      size_t bytesWritten,
+      std::unique_ptr<IOBuf> data,
+      WriteFlags flags);
+
+  void failWrite(const AsyncSocketException& ex);
+  void failAllWrites(const AsyncSocketException& ex);
+
+  bool empty() { return requestHead_ == nullptr; }
+
+ private:
+  explicit IoUringSendHandle(
+      EventBase* evb,
+      IoUringBackend* backend,
+      NetworkSocket fd,
+      const SocketAddress& addr,
+      IoUringSendCallback* callback);
+
+  explicit IoUringSendHandle(
+      EventBase* evb, IoUringBackend* backend, UniquePtr other);
+
+  void trySubmit();
+
+  class SendRequest;
+  void onSendStarted();
+  void onSendPartial(size_t bytesWritten);
+  void onSendComplete(size_t bytesWritten);
+  void onSendErr(int err);
+  void onReleaseIOBuf(
+      std::unique_ptr<IOBuf> data, AsyncWriter::ReleaseIOBufCallback* callback);
+
+  EventBase* evb_;
+  IoUringBackend* backend_;
+  NetworkSocket fd_;
+  SocketAddress addr_;
+  IoUringSendCallback* sendCallback_;
+
+  SendRequest* requestHead_{nullptr};
+  SendRequest* requestTail_{nullptr};
+
+  bool sendEnabled_{false};
+  Optional<SemiFuture<int>> detachedFuture_;
+};
+
+} // namespace folly
diff --git a/folly/folly/io/async/IoUringZeroCopyBufferPool.cpp b/folly/folly/io/async/IoUringZeroCopyBufferPool.cpp
--- a/folly/folly/io/async/IoUringZeroCopyBufferPool.cpp
+++ b/folly/folly/io/async/IoUringZeroCopyBufferPool.cpp
@@ -17,19 +17,150 @@
 #include <folly/io/async/IoUringZeroCopyBufferPool.h>
 
 #include <mutex>
+#include <queue>
 
 #include <folly/Conv.h>
+#include <folly/String.h>
 #include <folly/lang/Align.h>
 #include <folly/portability/SysMman.h>
+#include <folly/synchronization/DistributedMutex.h>
 
 #if FOLLY_HAS_LIBURING
 
+// TODO(davidhwei): Remove once liburing catches up and gets synced with
+// fbsource.
+#define IORING_REGISTER_ZCRX_CTRL 36
+
+enum zcrx_reg_flags {
+  ZCRX_REG_IMPORT = 1,
+};
+
+enum zcrx_ctrl_op {
+  ZCRX_CTRL_FLUSH_RQ,
+  ZCRX_CTRL_EXPORT,
+
+  __ZCRX_CTRL_LAST,
+};
+
+struct zcrx_ctrl_flush_rq {
+  __u64 __resv[6];
+};
+
+struct zcrx_ctrl_export {
+  __u32 zcrx_fd;
+  __u32 __resv1[11];
+};
+
+struct zcrx_ctrl {
+  __u32 zcrx_id;
+  __u32 op; /* see enum zcrx_ctrl_op */
+  __u64 __resv[2];
+
+  union {
+    struct zcrx_ctrl_export zc_export;
+    struct zcrx_ctrl_flush_rq zc_flush;
+  };
+};
+
 namespace folly {
 
+class IoUringZeroCopyBufferPoolImpl {
+ public:
+  friend class IoUringZeroCopyBufferPool;
+
+  struct Buffer {
+    uint64_t off;
+    uint32_t len;
+    IoUringZeroCopyBufferPoolImpl* pool;
+  };
+
+  explicit IoUringZeroCopyBufferPoolImpl(
+      IoUringZeroCopyBufferPool::Params params, bool test);
+
+  ~IoUringZeroCopyBufferPoolImpl();
+
+  IoUringZeroCopyBufferPoolImpl(IoUringZeroCopyBufferPoolImpl&&) = delete;
+  IoUringZeroCopyBufferPoolImpl(IoUringZeroCopyBufferPoolImpl const&) = delete;
+  IoUringZeroCopyBufferPoolImpl& operator=(IoUringZeroCopyBufferPoolImpl&&) =
+      delete;
+  IoUringZeroCopyBufferPoolImpl& operator=(
+      IoUringZeroCopyBufferPoolImpl const&) = delete;
+
+  void destroy() noexcept;
+
+  std::unique_ptr<IOBuf> getIoBuf(
+      const struct io_uring_cqe* cqe,
+      const struct io_uring_zcrx_cqe* rcqe) noexcept;
+  void returnBuffer(Buffer* buf) noexcept;
+
+  // For testing
+  uint32_t* getHead() const noexcept { return rqRing_.khead; }
+  uint32_t getRingUsedCount() const noexcept {
+    return rqTail_ - io_uring_smp_load_acquire(rqRing_.khead);
+  }
+  uint32_t getRingFreeCount() const noexcept {
+    return rqEntries_ - getRingUsedCount();
+  }
+  size_t getPendingBuffersSize() const noexcept {
+    return pendingBuffers_.size();
+  }
+  uint32_t getFlushThreshold() const noexcept { return flushThreshold_; }
+  uint16_t getAndResetFlushFailures() noexcept {
+    return flushFailures_.exchange(0, std::memory_order_relaxed);
+  }
+  uint16_t getAndResetFlushCount() noexcept {
+    return flushCount_.exchange(0, std::memory_order_relaxed);
+  }
+
+ private:
+  void mapMemory();
+  void initialRegister(uint32_t ifindex, uint16_t queueId);
+  void delayedDestroy(uint32_t refs) noexcept;
+  uint32_t getRingQueuedCount() const noexcept;
+  void writeBufferToRing(Buffer* buffer) noexcept;
+  void flushRefillQueue() noexcept;
+
+  struct io_uring* ring_{nullptr};
+  size_t pageSize_{0};
+  uint32_t rqEntries_{0};
+
+  void* bufArea_{nullptr};
+  size_t bufAreaSize_{0};
+  std::vector<Buffer> buffers_;
+  void* rqRingArea_{nullptr};
+  size_t rqRingAreaSize_{0};
+  struct io_uring_zcrx_rq rqRing_{};
+  uint64_t rqAreaToken_{0};
+  uint32_t rqTail_{0};
+  unsigned rqMask_{0};
+  uint32_t id_{0};
+  std::atomic<uint32_t> bufDispensed_{0};
+
+  folly::DistributedMutex mutex_;
+  std::atomic<bool> wantsShutdown_{false};
+  uint32_t shutdownReferences_{0};
+  std::queue<Buffer*> pendingBuffers_;
+  uint32_t flushThreshold_{128};
+  std::atomic<uint16_t> flushFailures_{0};
+  std::atomic<uint16_t> flushCount_{0};
+};
+
+struct ImplDeleter {
+  void operator()(IoUringZeroCopyBufferPoolImpl* impl) const noexcept {
+    if (impl) {
+      impl->destroy();
+    }
+  }
+};
+
 namespace {
+struct RingQueue {
+  uint32_t head;
+  uint32_t tail;
+};
 
 size_t getRefillRingSize(size_t rqEntries) {
-  size_t size = rqEntries * sizeof(io_uring_zcrx_rqe);
+  size_t size = rqEntries * sizeof(struct io_uring_zcrx_rqe);
   // Include space for the header (head/tail/etc.)
   size_t pageSize = sysconf(_SC_PAGESIZE);
   size += pageSize;
@@ -40,20 +171,8 @@
 
 } // namespace
 
-void IoUringZeroCopyBufferPool::Deleter::operator()(
-    IoUringZeroCopyBufferPool* base) {
-  if (base) {
-    base->destroy();
-  }
-}
-
-IoUringZeroCopyBufferPool::UniquePtr IoUringZeroCopyBufferPool::create(
-    Params params) {
-  return IoUringZeroCopyBufferPool::UniquePtr(
-      new IoUringZeroCopyBufferPool(params));
-}
-
-IoUringZeroCopyBufferPool::IoUringZeroCopyBufferPool(Params params)
+IoUringZeroCopyBufferPoolImpl::IoUringZeroCopyBufferPoolImpl(
+    IoUringZeroCopyBufferPool::Params params, bool test)
     : ring_(params.ring),
       pageSize_(params.pageSize),
       rqEntries_(params.rqEntries),
@@ -63,35 +182,61 @@
   for (auto& buf : buffers_) {
     buf.pool = this;
   }
-  if (ring_ != nullptr) {
+  mapMemory();
+
+  // Calculate flush threshold to bound pending queue growth.
+  //
+  // When pending buffers reach this threshold, we flush to the kernel which
+  // drains entries from the refill ring, freeing space for pending buffers.
+  //
+  // Threshold = max(128, min(rqEntries/4, numPages/20)):
+  // - rqEntries/4 (25% of ring): scales with kernel's drain capacity per flush
+  // - numPages/20 (5% of pool): caps memory sitting idle in pending queue
+  // - min(): use the smaller limit to respect both constraints
+  // - max(128): floor of 4 × ZCRX_FLUSH_BATCH to ensure batching efficiency
+  constexpr uint32_t kKernelFlushBatch = 32;
+  constexpr uint32_t kMinFlushThreshold = 4 * kKernelFlushBatch;
+  flushThreshold_ = std::max(
+      kMinFlushThreshold,
+      std::min(
+          static_cast<uint32_t>(rqEntries_ / 4), // 25% of ring
+          static_cast<uint32_t>(params.numPages / 20))); // 5% of pool
+
+  if (!test) {
     initialRegister(params.ifindex, params.queueId);
   } else {
-    // rqRing_ is set up using information that the kernel fills in via
-    // io_uring_register_ifq(). Unit tests do not do this, so fake the rqRing_,
-    // specifically ktail and rqes array
-    mapMemory();
-    rqRing_.khead = nullptr;
+    // rqRing_ is normally set up using information that the kernel fills in via
+    // io_uring_register_ifq(). Unit tests do not do this, so fake it.
+    rqRing_.khead = reinterpret_cast<uint32_t*>(
+        (static_cast<char*>(rqRingArea_) + offsetof(RingQueue, head)));
     rqRing_.ktail = reinterpret_cast<uint32_t*>(
-        static_cast<char*>(rqRingArea_) +
-        (rqEntries_ * sizeof(io_uring_zcrx_rqe)));
-    rqRing_.rqes = static_cast<io_uring_zcrx_rqe*>(rqRingArea_);
+        (static_cast<char*>(rqRingArea_) + offsetof(RingQueue, tail)));
+    rqRing_.rqes = reinterpret_cast<struct io_uring_zcrx_rqe*>(
+        static_cast<char*>(rqRingArea_) + sizeof(RingQueue));
     rqRing_.rq_tail = 0;
     rqRing_.ring_entries = rqEntries_;
   }
 }
 
-void IoUringZeroCopyBufferPool::destroy() noexcept {
+IoUringZeroCopyBufferPoolImpl::~IoUringZeroCopyBufferPoolImpl() {
+  ::munmap(bufArea_, bufAreaSize_ + rqRingAreaSize_);
+}
+
+void IoUringZeroCopyBufferPoolImpl::destroy() noexcept {
   std::unique_lock lock{mutex_};
   DCHECK(bufDispensed_ >= rqTail_);
-  auto remaining = bufDispensed_ - rqTail_;
+  auto remaining = bufDispensed_.load(std::memory_order_relaxed) - rqTail_;
+  // Drain refs in overflow queue
+  remaining -= pendingBuffers_.size();
   shutdownReferences_ = remaining;
   wantsShutdown_ = true;
   lock.unlock();
   delayedDestroy(remaining);
 }
 
-std::unique_ptr<IOBuf> IoUringZeroCopyBufferPool::getIoBuf(
-    const io_uring_cqe* cqe, const io_uring_zcrx_cqe* rcqe) noexcept {
+std::unique_ptr<IOBuf> IoUringZeroCopyBufferPoolImpl::getIoBuf(
+    const struct io_uring_cqe* cqe,
+    const struct io_uring_zcrx_cqe* rcqe) noexcept {
   // By the time the pool is being destroyed, IoUringBackend has already drained
   // all requests so there won't be any more calls to getIoBuf().
   DCHECK(!wantsShutdown_);
@@ -100,7 +245,7 @@
 
   auto freeFn = [](void*, void* userData) {
     auto buffer =
-        reinterpret_cast<IoUringZeroCopyBufferPool::Buffer*>(userData);
+        reinterpret_cast<IoUringZeroCopyBufferPoolImpl::Buffer*>(userData);
     DCHECK(buffer->pool);
     buffer->pool->returnBuffer(buffer);
   };
@@ -116,12 +261,15 @@
       length,
       freeFn,
       &buffers_[i]);
-  // This method is only called from an EVB so there is no synchronization.
-  bufDispensed_++;
+  // The underlying buffers are shared between userspace and kernel. The IOBufs
+  // only 'wrap' the data and is read-only. Mark as shared such that downstream
+  // users of this IOBuf do not try to destructively modify the data.
+  ret->markExternallySharedOne();
+  bufDispensed_.fetch_add(1, std::memory_order_relaxed);
   return ret;
 }
 
-void IoUringZeroCopyBufferPool::mapMemory() {
+void IoUringZeroCopyBufferPoolImpl::mapMemory() {
   bufArea_ = ::mmap(
       nullptr,
       bufAreaSize_ + rqRingAreaSize_,
@@ -130,66 +278,70 @@
       -1,
       0);
   if (bufArea_ == MAP_FAILED) {
-    throw std::runtime_error(folly::to<std::string>(
-        "IoUringZeroCopyBufferPool failed to mmap size ",
-        bufAreaSize_ + rqRingAreaSize_));
+    throw std::runtime_error(
+        folly::to<std::string>(
+            "IoUringZeroCopyBufferPool failed to mmap size ",
+            bufAreaSize_ + rqRingAreaSize_));
   }
 
   rqRingArea_ = static_cast<char*>(bufArea_) + bufAreaSize_;
 }
 
-FOLLY_PUSH_WARNING
-FOLLY_GNU_DISABLE_WARNING("-Wmissing-designated-field-initializers")
-
-void IoUringZeroCopyBufferPool::initialRegister(
+void IoUringZeroCopyBufferPoolImpl::initialRegister(
     uint32_t ifindex, uint16_t queueId) {
-  mapMemory();
-
-  io_uring_region_desc regionReg = {
-      .user_addr = reinterpret_cast<uint64_t>(rqRingArea_),
-      .size = rqRingAreaSize_,
-      .flags = IORING_MEM_REGION_TYPE_USER,
-  };
-
-  io_uring_zcrx_area_reg areaReg = {
-      .addr = reinterpret_cast<uint64_t>(bufArea_),
-      .len = bufAreaSize_,
-      .flags = 0,
-  };
+  struct io_uring_region_desc regionReg{};
+  regionReg.user_addr = reinterpret_cast<uint64_t>(rqRingArea_);
+  regionReg.size = rqRingAreaSize_;
+  regionReg.flags = IORING_MEM_REGION_TYPE_USER;
 
-  io_uring_zcrx_ifq_reg ifqReg = {
-      .if_idx = ifindex,
-      .if_rxq = queueId,
-      .rq_entries = rqEntries_,
-      .area_ptr = reinterpret_cast<uint64_t>(&areaReg),
-      .region_ptr = reinterpret_cast<uint64_t>(&regionReg),
-  };
+  struct io_uring_zcrx_area_reg areaReg{};
+  areaReg.addr = reinterpret_cast<uint64_t>(bufArea_);
+  areaReg.len = bufAreaSize_;
 
-  FOLLY_POP_WARNING
+  struct io_uring_zcrx_ifq_reg ifqReg{};
+  ifqReg.if_idx = ifindex;
+  ifqReg.if_rxq = queueId;
+  ifqReg.rq_entries = rqEntries_;
+  ifqReg.area_ptr = reinterpret_cast<uint64_t>(&areaReg);
+  ifqReg.region_ptr = reinterpret_cast<uint64_t>(&regionReg);
 
   auto ret = io_uring_register_ifq(ring_, &ifqReg);
   if (ret) {
     ::munmap(bufArea_, bufAreaSize_ + rqRingAreaSize_);
-    throw std::runtime_error(fmt::format(
-        "IoUringZeroCopyBufferPool failed io_uring_register_ifq: {} {}",
-        ret,
-        ::strerror(ret)));
+    throw std::runtime_error(
+        fmt::format(
+            "IoUringZeroCopyBufferPool failed io_uring_register_ifq: {} {}",
+            ret,
+            folly::errnoStr(ret)));
   }
 
   rqRing_.khead = reinterpret_cast<uint32_t*>(
       (static_cast<char*>(rqRingArea_) + ifqReg.offsets.head));
   rqRing_.ktail = reinterpret_cast<uint32_t*>(
       (static_cast<char*>(rqRingArea_) + ifqReg.offsets.tail));
-  rqRing_.rqes = reinterpret_cast<io_uring_zcrx_rqe*>(
+  rqRing_.rqes = reinterpret_cast<struct io_uring_zcrx_rqe*>(
       static_cast<char*>(rqRingArea_) + ifqReg.offsets.rqes);
   rqRing_.rq_tail = 0;
   rqRing_.ring_entries = ifqReg.rq_entries;
 
   rqAreaToken_ = areaReg.rq_area_token;
   rqMask_ = ifqReg.rq_entries - 1;
+  id_ = ifqReg.zcrx_id;
 }
 
-void IoUringZeroCopyBufferPool::returnBuffer(Buffer* buffer) noexcept {
+uint32_t IoUringZeroCopyBufferPoolImpl::getRingQueuedCount() const noexcept {
+  return rqTail_ - io_uring_smp_load_acquire(rqRing_.khead);
+}
+
+void IoUringZeroCopyBufferPoolImpl::writeBufferToRing(Buffer* buffer) noexcept {
+  uint32_t myTail = rqTail_++;
+
+  struct io_uring_zcrx_rqe* rqe = &rqRing_.rqes[myTail & rqMask_];
+  rqe->off = (buffer->off & ~IORING_ZCRX_AREA_MASK) | rqAreaToken_;
+  rqe->len = buffer->len;
+}
+
+void IoUringZeroCopyBufferPoolImpl::returnBuffer(Buffer* buffer) noexcept {
   std::unique_lock lock{mutex_};
   if (FOLLY_UNLIKELY(wantsShutdown_)) {
     auto refs = --shutdownReferences_;
@@ -198,23 +350,162 @@
     return;
   }
 
-  uint32_t myTail = static_cast<uint32_t>(rqTail_++);
-  uint32_t nextTail = myTail + 1;
+  uint32_t startTail = rqTail_;
 
-  io_uring_zcrx_rqe* rqe;
-  rqe = &rqRing_.rqes[myTail & rqMask_];
-  rqe->off = (buffer->off & ~IORING_ZCRX_AREA_MASK) | rqAreaToken_;
-  rqe->len = buffer->len;
+  // Trigger flush when pending buffers reach threshold computed from:
+  // max(128, min(25% of ring, 5% of pool))
+  // This bounds pending queue growth per Little's Law: L = λW
+  if (pendingBuffers_.size() >= flushThreshold_) {
+    flushRefillQueue();
+  }
 
-  // Update the tail and make visible to kernel
-  io_uring_smp_store_release(rqRing_.ktail, nextTail);
+  uint32_t queueLength = getRingQueuedCount();
+  uint32_t slots = rqRing_.ring_entries - queueLength;
+  auto numToProcess =
+      std::min(pendingBuffers_.size(), static_cast<size_t>(slots));
+  for (size_t i = 0; i < numToProcess; i++) {
+    writeBufferToRing(pendingBuffers_.front());
+    pendingBuffers_.pop();
+  }
+
+  if (numToProcess < slots) {
+    writeBufferToRing(buffer);
+  } else {
+    pendingBuffers_.push(buffer);
+  }
+
+  if (rqTail_ != startTail) {
+    io_uring_smp_store_release(rqRing_.ktail, rqTail_);
+  }
 }
 
-void IoUringZeroCopyBufferPool::delayedDestroy(uint32_t refs) noexcept {
+void IoUringZeroCopyBufferPoolImpl::delayedDestroy(uint32_t refs) noexcept {
   if (refs == 0) {
-    ::munmap(bufArea_, bufAreaSize_ + rqRingAreaSize_);
     delete this;
   }
+}
+
+void IoUringZeroCopyBufferPoolImpl::flushRefillQueue() noexcept {
+  // ring_ is nullptr in unit tests which don't have a real io_uring.
+  if (ring_ == nullptr) {
+    return;
+  }
+  struct zcrx_ctrl ctrl{};
+  ctrl.zcrx_id = id_;
+  ctrl.op = ZCRX_CTRL_FLUSH_RQ;
+
+  // Best-effort flush: on failure (e.g., -EOPNOTSUPP on older kernels),
+  // buffers remain in pendingBuffers_ and are processed when ring space
+  // becomes available naturally.
+  int ret =
+      io_uring_register(ring_->ring_fd, IORING_REGISTER_ZCRX_CTRL, &ctrl, 0);
+  flushCount_.fetch_add(1, std::memory_order_relaxed);
+  if (ret < 0) {
+    flushFailures_.fetch_add(1, std::memory_order_relaxed);
+    LOG_EVERY_N(WARNING, 100)
+        << "Zero copy receive refill queue flush failed: "
+        << folly::errnoStr(-ret);
+  }
+}
+
+/*static*/ IoUringZeroCopyBufferPool::UniquePtr
+IoUringZeroCopyBufferPool::create(Params params) {
+  return IoUringZeroCopyBufferPool::UniquePtr(
+      new IoUringZeroCopyBufferPool(params));
+}
+
+/*static*/ IoUringZeroCopyBufferPool::UniquePtr
+IoUringZeroCopyBufferPool::importHandle(
+    ExportHandle handle, struct io_uring* ring) {
+  return IoUringZeroCopyBufferPool::UniquePtr(
+      new IoUringZeroCopyBufferPool(std::move(handle), ring));
+}
+
+IoUringZeroCopyBufferPool::IoUringZeroCopyBufferPool(Params params)
+    : ring_(params.ring),
+      impl_(new IoUringZeroCopyBufferPoolImpl(params, false), ImplDeleter{}) {
+  zcrxId_ = impl_->id_;
+}
+
+IoUringZeroCopyBufferPool::IoUringZeroCopyBufferPool(Params params, TestTag)
+    : ring_(params.ring),
+      impl_(new IoUringZeroCopyBufferPoolImpl(params, true), ImplDeleter{}) {}
+
+IoUringZeroCopyBufferPool::IoUringZeroCopyBufferPool(
+    ExportHandle handle, struct io_uring* ring)
+    : ring_(ring) {
+  struct io_uring_zcrx_ifq_reg ifqReg{};
+  ifqReg.if_idx = static_cast<uint32_t>(handle.zcrxFd_);
+  ifqReg.flags = ZCRX_REG_IMPORT;
+
+  auto ret = io_uring_register_ifq(ring_, &ifqReg);
+  if (ret) {
+    throw std::runtime_error(
+        fmt::format(
+            "IoUringZeroCopyBufferPool failed io_uring_register_ifq: {} {}",
+            ret,
+            folly::errnoStr(ret)));
+  }
+
+  zcrxId_ = ifqReg.zcrx_id;
+  zcrxFd_ = handle.zcrxFd_;
+  impl_ = std::move(handle.impl_);
+}
+
+IoUringZeroCopyBufferPool::~IoUringZeroCopyBufferPool() {
+  if (zcrxFd_ >= 0) {
+    close(zcrxFd_);
+  }
+}
+
+IoUringZeroCopyBufferPool::ExportHandle
+IoUringZeroCopyBufferPool::exportHandle() const {
+  if (impl_->ring_ != ring_) {
+    throw std::runtime_error(
+        "Cannot export a handle from a non-owning IoUringZeroCopyBufferPool");
+  }
+
+  struct zcrx_ctrl ctrl{};
+  ctrl.zcrx_id = impl_->id_;
+  ctrl.op = ZCRX_CTRL_EXPORT;
+  auto zcrxFd =
+      io_uring_register(ring_->ring_fd, IORING_REGISTER_ZCRX_CTRL, &ctrl, 0);
+
+  return ExportHandle(zcrxFd, impl_);
+}
+
+std::unique_ptr<IOBuf> IoUringZeroCopyBufferPool::getIoBuf(
+    const struct io_uring_cqe* cqe,
+    const struct io_uring_zcrx_cqe* rcqe) noexcept {
+  return impl_->getIoBuf(cqe, rcqe);
+}
+
+uint32_t* IoUringZeroCopyBufferPool::getHead() const noexcept {
+  return impl_->getHead();
+}
+
+uint32_t IoUringZeroCopyBufferPool::getRingUsedCount() const noexcept {
+  return impl_->getRingUsedCount();
+}
+
+uint32_t IoUringZeroCopyBufferPool::getRingFreeCount() const noexcept {
+  return impl_->getRingFreeCount();
+}
+
+size_t IoUringZeroCopyBufferPool::getPendingBuffersSize() const noexcept {
+  return impl_->getPendingBuffersSize();
+}
+
+uint32_t IoUringZeroCopyBufferPool::getFlushThreshold() const noexcept {
+  return impl_->getFlushThreshold();
+}
+
+uint16_t IoUringZeroCopyBufferPool::getAndResetFlushFailures() noexcept {
+  return impl_->getAndResetFlushFailures();
+}
+
+uint16_t IoUringZeroCopyBufferPool::getAndResetFlushCount() noexcept {
+  return impl_->getAndResetFlushCount();
 }
 
 } // namespace folly
diff --git a/folly/folly/io/async/IoUringZeroCopyBufferPool.h b/folly/folly/io/async/IoUringZeroCopyBufferPool.h
--- a/folly/folly/io/async/IoUringZeroCopyBufferPool.h
+++ b/folly/folly/io/async/IoUringZeroCopyBufferPool.h
@@ -16,28 +16,26 @@
 
 #pragma once
 
+#include <memory>
 #include <folly/io/IOBuf.h>
 #include <folly/io/async/Liburing.h>
-#include <folly/synchronization/DistributedMutex.h>
 
 #if FOLLY_HAS_LIBURING
-
 FOLLY_PUSH_WARNING
 FOLLY_CLANG_DISABLE_WARNING("-Wnested-anon-types")
 FOLLY_CLANG_DISABLE_WARNING("-Wzero-length-array")
+FOLLY_GCC_DISABLE_WARNING("-Wshadow")
 #include <liburing.h> // @manual
 FOLLY_POP_WARNING
 
 namespace folly {
 
+class IoUringZeroCopyBufferPoolImpl;
+
 class IoUringZeroCopyBufferPool {
  public:
-  struct Deleter {
-    void operator()(IoUringZeroCopyBufferPool* base);
-  };
-
   struct Params {
-    io_uring* ring;
+    struct io_uring* ring;
     size_t numPages;
     size_t pageSize;
     uint32_t rqEntries;
@@ -45,59 +43,66 @@
     uint16_t queueId;
   };
 
-  // Only support heap construction with a custom Deleter. This is to avoid
-  // deleting the object until all buffers have been returned.
-  using UniquePtr = std::unique_ptr<IoUringZeroCopyBufferPool, Deleter>;
-  static IoUringZeroCopyBufferPool::UniquePtr create(Params params);
+  struct ExportHandle {
+    explicit ExportHandle(
+        int zcrxFd, std::shared_ptr<IoUringZeroCopyBufferPoolImpl> impl)
+        : zcrxFd_(zcrxFd), impl_(std::move(impl)) {}
 
-  ~IoUringZeroCopyBufferPool() = default;
+    ~ExportHandle() = default;
 
-  void destroy() noexcept;
+    ExportHandle(ExportHandle&&) = default;
+    ExportHandle& operator=(ExportHandle&&) = default;
+    ExportHandle(const ExportHandle&) = delete;
+    ExportHandle& operator=(const ExportHandle&) = delete;
+
+   private:
+    friend class IoUringZeroCopyBufferPool;
+
+    int zcrxFd_;
+    std::shared_ptr<IoUringZeroCopyBufferPoolImpl> impl_;
+  };
+
+  using UniquePtr = std::unique_ptr<IoUringZeroCopyBufferPool>;
+  static UniquePtr create(Params params);
+  static UniquePtr importHandle(ExportHandle handle, struct io_uring* ring);
+
+  ExportHandle exportHandle() const;
+
+  ~IoUringZeroCopyBufferPool();
+
   std::unique_ptr<IOBuf> getIoBuf(
-      const io_uring_cqe* cqe, const io_uring_zcrx_cqe* rcqe) noexcept;
+      const struct io_uring_cqe* cqe,
+      const struct io_uring_zcrx_cqe* rcqe) noexcept;
 
  private:
   explicit IoUringZeroCopyBufferPool(Params params);
 
+  struct TestTag {};
+  explicit IoUringZeroCopyBufferPool(Params params, TestTag);
+
+  explicit IoUringZeroCopyBufferPool(
+      ExportHandle handle, struct io_uring* ring);
+
   IoUringZeroCopyBufferPool(IoUringZeroCopyBufferPool&&) = delete;
   IoUringZeroCopyBufferPool(IoUringZeroCopyBufferPool const&) = delete;
   IoUringZeroCopyBufferPool& operator=(IoUringZeroCopyBufferPool&&) = delete;
   IoUringZeroCopyBufferPool& operator=(IoUringZeroCopyBufferPool const&) =
       delete;
 
-  struct Buffer {
-    uint64_t off;
-    uint32_t len;
-    IoUringZeroCopyBufferPool* pool;
-  };
-
-  void mapMemory();
-  void initialRegister(uint32_t ifindex, uint16_t queueId);
-
-  void returnBuffer(Buffer* buf) noexcept;
-
-  void delayedDestroy(uint32_t refs) noexcept;
-
-  io_uring* ring_{nullptr};
-  size_t pageSize_{0};
-  uint32_t rqEntries_{0};
-
-  void* bufArea_{nullptr};
-  size_t bufAreaSize_{0};
-  std::vector<Buffer> buffers_;
-  void* rqRingArea_{nullptr};
-  size_t rqRingAreaSize_{0};
-  // Ring buffer shared between kernel and userspace
-  // Constructed in initialRegister()
-  io_uring_zcrx_rq rqRing_;
-  uint64_t rqAreaToken_{0};
-  uint64_t rqTail_{0};
-  unsigned rqMask_{0};
-  uint64_t bufDispensed_{0};
+  // For testing
+  friend class IoUringZeroCopyBufferPoolTestHelper;
+  uint32_t* getHead() const noexcept;
+  uint32_t getRingUsedCount() const noexcept;
+  uint32_t getRingFreeCount() const noexcept;
+  size_t getPendingBuffersSize() const noexcept;
+  uint32_t getFlushThreshold() const noexcept;
+  uint16_t getAndResetFlushFailures() noexcept;
+  uint16_t getAndResetFlushCount() noexcept;
 
-  folly::DistributedMutex mutex_;
-  std::atomic<bool> wantsShutdown_{false};
-  uint32_t shutdownReferences_{0};
+  struct io_uring* ring_{nullptr};
+  std::shared_ptr<IoUringZeroCopyBufferPoolImpl> impl_;
+  int zcrxId_{-1};
+  int zcrxFd_{-1};
 };
 
 } // namespace folly
diff --git a/folly/folly/io/async/MuxIOThreadPoolExecutor.cpp b/folly/folly/io/async/MuxIOThreadPoolExecutor.cpp
--- a/folly/folly/io/async/MuxIOThreadPoolExecutor.cpp
+++ b/folly/folly/io/async/MuxIOThreadPoolExecutor.cpp
@@ -126,10 +126,11 @@
 
 void MuxIOThreadPoolExecutor::validateNumThreads(size_t numThreads) {
   if (numThreads == 0 || numThreads > numEventBases_) {
-    throw std::invalid_argument(fmt::format(
-        "Unsupported number of threads: {} (with {} EventBases)",
-        numThreads,
-        numEventBases_));
+    throw std::invalid_argument(
+        fmt::format(
+            "Unsupported number of threads: {} (with {} EventBases)",
+            numThreads,
+            numEventBases_));
   }
 }
 
@@ -184,7 +185,7 @@
     o->threadStopped(thread.get());
   }
   threadList_.remove(thread);
-  stoppedThreads_.add(thread);
+  stoppedThreads_.add(std::move(thread));
 }
 
 MuxIOThreadPoolExecutor::EvbState& MuxIOThreadPoolExecutor::pickEvbState() {
diff --git a/folly/folly/io/async/NotificationQueue.h b/folly/folly/io/async/NotificationQueue.h
--- a/folly/folly/io/async/NotificationQueue.h
+++ b/folly/folly/io/async/NotificationQueue.h
@@ -402,8 +402,8 @@
    */
   template <typename InputIteratorT>
   void putMessages(InputIteratorT first, InputIteratorT last) {
-    typedef typename std::iterator_traits<InputIteratorT>::iterator_category
-        IterCategory;
+    using IterCategory =
+        typename std::iterator_traits<InputIteratorT>::iterator_category;
     putMessagesImpl(first, last, IterCategory());
   }
 
diff --git a/folly/folly/io/async/Request.cpp b/folly/folly/io/async/Request.cpp
--- a/folly/folly/io/async/Request.cpp
+++ b/folly/folly/io/async/Request.cpp
@@ -21,7 +21,15 @@
 #include <folly/tracing/StaticTracepoint.h>
 
 namespace folly {
+// Thread-local cache of raw RequestContext.
+// Updated by setContext() and setShallowCopyContext() to avoid calling
+// SingletonThreadLocal::get() in locations requiring async-signal-safe access.
+thread_local RequestContext* gAsyncSignalSafeRequestContextCache = nullptr;
 
+RequestContext* getCachedRequestContext() {
+  return gAsyncSignalSafeRequestContextCache;
+}
+
 RequestToken::RequestToken(const std::string& str) {
   auto& cache = getCache();
   {
@@ -227,7 +235,7 @@
   }
 }
 
-class FOLLY_NODISCARD RequestContext::State::LockGuard {
+class [[nodiscard]] RequestContext::State::LockGuard {
  public:
   explicit LockGuard(RequestContext::State& state)
       : state_(state), lock_(state.mutex_) {}
@@ -655,21 +663,44 @@
       staticCtx.rootId.store(0, std::memory_order_relaxed);
     }
   }
+  gAsyncSignalSafeRequestContextCache = staticCtx.requestContext.get();
+  // Notify the Watchers via the registry
+  getWatcherRegistry().invokeWatchers(prevCtx, staticCtx.requestContext);
   return prevCtx;
 }
 
-namespace {
-thread_local bool getStaticContextCalled = false;
+/* static */ RequestContext::SetContextWatcherRegistry&
+RequestContext::getWatcherRegistry() {
+  static SetContextWatcherRegistry registry;
+
+  return registry;
 }
 
+/* static */ void RequestContext::addSetContextWatcher(
+    RequestContext::SetContextWatcherSig& func) {
+  getWatcherRegistry().addWatcher(func);
+}
+
+/* static */ std::shared_ptr<RequestContext> RequestContext::saveContext() {
+  auto* staticContext = tryGetStaticContext();
+  return staticContext ? staticContext->requestContext : nullptr;
+}
+
+RequestContext::StaticContext::~StaticContext() {
+  // If there is an active request context, reset requestContext before
+  // destroying it, as RequestData destructors (or onClear()) could try to
+  // access the current request context and copy a shared_ptr while being
+  // destroyed.
+  std::ignore = std::exchange(requestContext, {});
+}
+
 /* static */ RequestContext::StaticContext& RequestContext::getStaticContext() {
-  getStaticContextCalled = true;
   return StaticContextThreadLocal::get();
 }
 
 /* static */ RequestContext::StaticContext*
 RequestContext::tryGetStaticContext() {
-  return getStaticContextCalled ? &StaticContextThreadLocal::get() : nullptr;
+  return StaticContextThreadLocal::try_get();
 }
 
 /* static */ RequestContext::StaticContextAccessor
@@ -699,16 +730,20 @@
   // Do not use setContext to avoid global set/unset
   // Also rootId does not change so do not bother setting it.
   std::swap(child, parent);
+  gAsyncSignalSafeRequestContextCache = parent.get();
+  getWatcherRegistry().invokeWatchers(child, parent);
   return child;
 }
 
 /* static */ RequestContext* RequestContext::get() {
-  auto& context = getStaticContext().requestContext;
-  if (!context) {
-    static RequestContext defaultContext(0);
-    return std::addressof(defaultContext);
+  if (auto* staticContext = tryGetStaticContext()) {
+    if (auto& context = staticContext->requestContext) {
+      return context.get();
+    }
   }
-  return context.get();
+
+  static RequestContext defaultContext(0);
+  return std::addressof(defaultContext);
 }
 
 /* static */ RequestContext* RequestContext::try_get() {
@@ -717,5 +752,11 @@
   }
   return nullptr;
 }
+
+#ifndef NDEBUG
+DCheckRequestContextRestoredGuard::~DCheckRequestContextRestoredGuard() {
+  CHECK_EQ(prev_.get(), RequestContext::try_get());
+}
+#endif
 
 } // namespace folly
diff --git a/folly/folly/io/async/Request.h b/folly/folly/io/async/Request.h
--- a/folly/folly/io/async/Request.h
+++ b/folly/folly/io/async/Request.h
@@ -297,11 +297,62 @@
   static std::shared_ptr<RequestContext> setContext(
       std::shared_ptr<RequestContext>&& newCtx_);
 
-  static std::shared_ptr<RequestContext> saveContext() {
-    return getStaticContext().requestContext;
-  }
+  // Theses Watcher functions will be called after a setContext with the prev
+  // and current context
+  using SetContextWatcherSig = void(
+      const std::shared_ptr<RequestContext>& prev,
+      const std::shared_ptr<RequestContext>& ctx);
+  static void addSetContextWatcher(SetContextWatcherSig& func);
 
  private:
+  struct SetContextWatcherRegistry {
+    using Sig = SetContextWatcherSig;
+
+    struct Watcher {
+      Sig* func_;
+      Watcher* next_{nullptr};
+
+      explicit Watcher(Sig& func) : func_(&func) {}
+    };
+
+    std::atomic<Watcher*> watchers_{nullptr};
+
+    void addWatcher(Sig& func) {
+      auto watcherPtr = new Watcher(func);
+      auto* head = watchers_.load(std::memory_order_relaxed);
+      do {
+        watcherPtr->next_ = head;
+      } while (!watchers_.compare_exchange_weak(
+          head,
+          watcherPtr,
+          std::memory_order_acq_rel,
+          std::memory_order_relaxed));
+    }
+
+    void invokeWatchers(
+        const std::shared_ptr<RequestContext>& prev,
+        const std::shared_ptr<RequestContext>& ctx) {
+      auto* watcher = watchers_.load(std::memory_order_acquire);
+      while (watcher != nullptr) {
+        watcher->func_(prev, ctx);
+        watcher = watcher->next_;
+      }
+    }
+
+    ~SetContextWatcherRegistry() {
+      auto* watcher = watchers_.exchange(nullptr, std::memory_order_acquire);
+      while (watcher != nullptr) {
+        delete std::exchange(watcher, watcher->next_);
+      }
+    }
+  };
+
+  static SetContextWatcherRegistry& getWatcherRegistry();
+
+ public:
+  static std::shared_ptr<RequestContext> saveContext();
+
+ private:
   struct Tag {};
   RequestContext(const RequestContext& ctx) = default;
 
@@ -311,6 +362,14 @@
   explicit RequestContext(intptr_t rootId);
 
   struct StaticContext {
+    StaticContext() = default;
+    ~StaticContext();
+
+    StaticContext(const StaticContext&) = delete;
+    StaticContext(StaticContext&&) = delete;
+    StaticContext& operator=(const StaticContext&) = delete;
+    StaticContext& operator=(StaticContext&&) = delete;
+
     std::shared_ptr<RequestContext> requestContext;
     std::atomic<intptr_t> rootId{0};
   };
@@ -616,6 +675,30 @@
   }
 
   std::shared_ptr<RequestContext> prev_;
+};
+
+// Debug-only guard that ensures that the current request context at destruction
+// is the same as the one at construction.
+class [[maybe_unused]] DCheckRequestContextRestoredGuard {
+#ifndef NDEBUG
+ public:
+  [[nodiscard]] DCheckRequestContextRestoredGuard()
+      : prev_(RequestContext::saveContext()) {}
+
+  ~DCheckRequestContextRestoredGuard();
+
+  DCheckRequestContextRestoredGuard(const DCheckRequestContextRestoredGuard&) =
+      delete;
+  DCheckRequestContextRestoredGuard& operator=(
+      const DCheckRequestContextRestoredGuard&) = delete;
+  DCheckRequestContextRestoredGuard(DCheckRequestContextRestoredGuard&&) =
+      delete;
+  DCheckRequestContextRestoredGuard& operator=(
+      DCheckRequestContextRestoredGuard&&) = delete;
+
+ private:
+  std::shared_ptr<RequestContext> prev_;
+#endif
 };
 
 template <class Traits>
diff --git a/folly/folly/io/async/SSLContext.cpp b/folly/folly/io/async/SSLContext.cpp
--- a/folly/folly/io/async/SSLContext.cpp
+++ b/folly/folly/io/async/SSLContext.cpp
@@ -20,11 +20,9 @@
 #include <folly/Memory.h>
 #include <folly/Random.h>
 #include <folly/SharedMutex.h>
-#include <folly/SpinLock.h>
 #include <folly/ssl/OpenSSLTicketHandler.h>
 #include <folly/ssl/PasswordCollector.h>
 #include <folly/ssl/SSLSessionManager.h>
-#include <folly/system/ThreadId.h>
 
 // ---------------------------------------------------------------------
 // SSLContext implementation
diff --git a/folly/folly/io/async/STTimerFDTimeoutManager.h b/folly/folly/io/async/STTimerFDTimeoutManager.h
--- a/folly/folly/io/async/STTimerFDTimeoutManager.h
+++ b/folly/folly/io/async/STTimerFDTimeoutManager.h
@@ -24,6 +24,10 @@
  public:
   explicit STTimerFDTimeoutManager(folly::EventBase* eventBase);
   ~STTimerFDTimeoutManager() override;
+  STTimerFDTimeoutManager(const STTimerFDTimeoutManager&) = delete;
+  STTimerFDTimeoutManager& operator=(const STTimerFDTimeoutManager&) = delete;
+  STTimerFDTimeoutManager(STTimerFDTimeoutManager&&) = delete;
+  STTimerFDTimeoutManager& operator=(STTimerFDTimeoutManager&&) = delete;
 
   /**
    * Attaches/detaches TimeoutManager to AsyncTimeout
diff --git a/folly/folly/io/async/ScopedEventBaseThread.cpp b/folly/folly/io/async/ScopedEventBaseThread.cpp
--- a/folly/folly/io/async/ScopedEventBaseThread.cpp
+++ b/folly/folly/io/async/ScopedEventBaseThread.cpp
@@ -31,7 +31,7 @@
     EventBaseManager* ebm,
     EventBase* eb,
     folly::Baton<>* stop,
-    const StringPiece& name) {
+    const StringPiece name) {
   if (!name.empty()) {
     folly::setThreadName(name);
   }
diff --git a/folly/folly/io/async/ScopedEventBaseThread.h b/folly/folly/io/async/ScopedEventBaseThread.h
--- a/folly/folly/io/async/ScopedEventBaseThread.h
+++ b/folly/folly/io/async/ScopedEventBaseThread.h
@@ -27,7 +27,7 @@
 class EventBaseManager;
 template <class Iter>
 class Range;
-typedef Range<const char*> StringPiece;
+using StringPiece = Range<const char*>;
 
 /**
  * ScopedEventBaseThread is a helper class that starts a new std::thread running
diff --git a/folly/folly/io/async/SimpleAsyncIO.cpp b/folly/folly/io/async/SimpleAsyncIO.cpp
--- a/folly/folly/io/async/SimpleAsyncIO.cpp
+++ b/folly/folly/io/async/SimpleAsyncIO.cpp
@@ -80,13 +80,12 @@
     default:
       // Should never happen...
       LOG(FATAL) << "unrecognized mode " << (int)cfg.mode_ << " requested";
-      break;
   }
 
   if (cfg.evb_) {
     initHandler(cfg.evb_, NetworkSocket::fromFd(asyncIO_->pollFd()));
   } else {
-    evb_ = std::make_unique<ScopedEventBaseThread>();
+    evb_ = std::make_unique<ScopedEventBaseThread>("SimpleAsyncIO");
     initHandler(
         evb_->getEventBase(), NetworkSocket::fromFd(asyncIO_->pollFd()));
   }
@@ -164,13 +163,17 @@
         CHECK(op_ == opHolder.get());
         int rc = op_->result();
 
-        completionExecutor_->add(
-            [rc, completor{std::move(completor)}]() mutable { completor(rc); });
+        auto completionExecutor = completionExecutor_;
 
-        // NB: the moment we put the opHolder, the destructor might delete the
-        // current instance. So do not access any member variables after this
-        // point! Also, obviously, do not access op_.
+        // make sure return opHolder before call completor: otherwise the caller
+        // might start another IO assuming this one is completed. NB: the moment
+        // we put the opHolder, the destructor might delete the current
+        // instance. So do not access any member variables after this point!
+        // Also, obviously, do not access op_.
         putOp(std::move(opHolder));
+
+        completionExecutor->add(
+            [rc, completor{std::move(completor)}]() mutable { completor(rc); });
       });
   asyncIO_->submit(op);
 }
diff --git a/folly/folly/io/async/TimeoutManager.h b/folly/folly/io/async/TimeoutManager.h
--- a/folly/folly/io/async/TimeoutManager.h
+++ b/folly/folly/io/async/TimeoutManager.h
@@ -33,8 +33,8 @@
  */
 class TimeoutManager {
  public:
-  typedef std::chrono::milliseconds timeout_type;
-  typedef std::chrono::microseconds timeout_type_high_res;
+  using timeout_type = std::chrono::milliseconds;
+  using timeout_type_high_res = std::chrono::microseconds;
 
   using Func = folly::Function<void()>;
 
diff --git a/folly/folly/io/async/TimerFD.cpp b/folly/folly/io/async/TimerFD.cpp
--- a/folly/folly/io/async/TimerFD.cpp
+++ b/folly/folly/io/async/TimerFD.cpp
@@ -30,7 +30,6 @@
 
 TimerFD::TimerFD(folly::EventBase* eventBase, int fd)
     : folly::EventHandler(eventBase, NetworkSocket::fromFd(fd)), fd_(fd) {
-  setEventCallback(this);
   if (fd_ > 0) {
     registerHandler(folly::EventHandler::READ | folly::EventHandler::PERSIST);
   }
@@ -88,18 +87,6 @@
     if (num == sizeof(data)) {
       onTimeout();
     }
-  }
-}
-
-void TimerFD::eventReadCallback(IoVec* ioVec, int res) {
-  // reset it
-  ioVec->timerData_ = 0;
-  // save it for future use
-  ioVecPtr_.reset(ioVec);
-
-  if (res == sizeof(ioVec->timerData_)) {
-    DestructorGuard dg(this);
-    onTimeout();
   }
 }
 
diff --git a/folly/folly/io/async/TimerFD.h b/folly/folly/io/async/TimerFD.h
--- a/folly/folly/io/async/TimerFD.h
+++ b/folly/folly/io/async/TimerFD.h
@@ -31,10 +31,7 @@
 namespace folly {
 #ifdef FOLLY_HAVE_TIMERFD
 // timerfd wrapper
-class TimerFD
-    : public folly::EventHandler,
-      public folly::EventReadCallback,
-      public DelayedDestruction {
+class TimerFD : public folly::EventHandler, public DelayedDestruction {
  public:
   explicit TimerFD(folly::EventBase* eventBase);
   ~TimerFD() override;
@@ -46,40 +43,10 @@
   // from folly::EventHandler
   void handlerReady(uint16_t events) noexcept override;
 
-  // from folly::EventReadCallback
-  folly::EventReadCallback::IoVec* allocateData() noexcept override {
-    auto* ret = ioVecPtr_.release();
-    return (ret ? ret : new IoVec(this));
-  }
-
  protected:
   void close();
 
  private:
-  struct IoVec : public folly::EventReadCallback::IoVec {
-    IoVec() = delete;
-    ~IoVec() override = default;
-    explicit IoVec(TimerFD* eventFd) {
-      arg_ = eventFd;
-      freeFunc_ = IoVec::free;
-      cbFunc_ = IoVec::cb;
-      data_.iov_base = &timerData_;
-      data_.iov_len = sizeof(timerData_);
-    }
-
-    static void free(EventReadCallback::IoVec* ioVec) { delete ioVec; }
-
-    static void cb(EventReadCallback::IoVec* ioVec, int res) {
-      reinterpret_cast<TimerFD*>(ioVec->arg_)
-          ->eventReadCallback(reinterpret_cast<IoVec*>(ioVec), res);
-    }
-
-    uint64_t timerData_{0};
-  };
-
-  void eventReadCallback(IoVec* ioVec, int res);
-  std::unique_ptr<IoVec> ioVecPtr_;
-
   TimerFD(folly::EventBase* eventBase, int fd);
   static int createTimerFd();
 
diff --git a/folly/folly/io/async/TimerFDTimeoutManager.h b/folly/folly/io/async/TimerFDTimeoutManager.h
--- a/folly/folly/io/async/TimerFDTimeoutManager.h
+++ b/folly/folly/io/async/TimerFDTimeoutManager.h
@@ -126,9 +126,8 @@
   // we want to reschedule the timers once we're done with the processing
   bool processingExpired_{false};
 
-  typedef boost::intrusive::
-      list<Callback, boost::intrusive::constant_time_size<false>>
-          CallbackList;
+  using CallbackList = boost::intrusive::
+      list<Callback, boost::intrusive::constant_time_size<false>>;
   std::map<std::chrono::microseconds, CallbackList> callbacks_;
   CallbackList inProgressList_;
 };
diff --git a/folly/folly/io/async/fdsock/AsyncFdSocket.cpp b/folly/folly/io/async/fdsock/AsyncFdSocket.cpp
--- a/folly/folly/io/async/fdsock/AsyncFdSocket.cpp
+++ b/folly/folly/io/async/fdsock/AsyncFdSocket.cpp
@@ -114,10 +114,11 @@
       DestructorGuard dg(this);
       AsyncSocketException ex(
           AsyncSocketException::BAD_ARGS,
-          withAddr(fmt::format(
-              "SeqNum of FDs did not match that of socket: {} vs {}",
-              fdsSeqNum,
-              sentFdsSeqNum_)));
+          withAddr(
+              fmt::format(
+                  "SeqNum of FDs did not match that of socket: {} vs {}",
+                  fdsSeqNum,
+                  sentFdsSeqNum_)));
       return failWrite(__func__, callback, 0, ex);
     }
     sentFdsSeqNum_ = detail::addSocketFdsSeqNum(sentFdsSeqNum_, fds.size());
diff --git a/folly/folly/io/async/observer/AsyncSocketObserverInterface.h b/folly/folly/io/async/observer/AsyncSocketObserverInterface.h
--- a/folly/folly/io/async/observer/AsyncSocketObserverInterface.h
+++ b/folly/folly/io/async/observer/AsyncSocketObserverInterface.h
@@ -144,7 +144,7 @@
      *   and ACK flags, and the other requesting just the SCHED flag, then we
      *   will add the TX, ACK, and SCHED flags to the request.
      */
-    FOLLY_NODISCARD const PrewriteRequest& getMergedRequest() const {
+    [[nodiscard]] const PrewriteRequest& getMergedRequest() const {
       return mergedRequest_;
     }
 
@@ -197,7 +197,7 @@
     /**
      * For WRITE events, returns if SCHED timestamp requested.
      */
-    FOLLY_NODISCARD bool schedTimestampRequestedOnWrite() const {
+    [[nodiscard]] bool schedTimestampRequestedOnWrite() const {
       CHECK_EQ(Type::WRITE, type);
       CHECK(maybeWriteFlags.has_value());
       return isSet(*maybeWriteFlags, WriteFlags::TIMESTAMP_SCHED);
@@ -206,7 +206,7 @@
     /**
      * For WRITE events, returns if TX timestamp requested.
      */
-    FOLLY_NODISCARD bool txTimestampRequestedOnWrite() const {
+    [[nodiscard]] bool txTimestampRequestedOnWrite() const {
       CHECK_EQ(Type::WRITE, type);
       CHECK(maybeWriteFlags.has_value());
       return isSet(*maybeWriteFlags, WriteFlags::TIMESTAMP_TX);
@@ -215,7 +215,7 @@
     /**
      * For WRITE events, returns if ACK timestamp requested.
      */
-    FOLLY_NODISCARD bool ackTimestampRequestedOnWrite() const {
+    [[nodiscard]] bool ackTimestampRequestedOnWrite() const {
       CHECK_EQ(Type::WRITE, type);
       CHECK(maybeWriteFlags.has_value());
       return isSet(*maybeWriteFlags, WriteFlags::TIMESTAMP_ACK);
diff --git a/folly/folly/io/async/ssl/SSLErrors.cpp b/folly/folly/io/async/ssl/SSLErrors.cpp
--- a/folly/folly/io/async/ssl/SSLErrors.cpp
+++ b/folly/folly/io/async/ssl/SSLErrors.cpp
@@ -120,10 +120,13 @@
     // Conservatively assume that this is an SSL error
     sslError = SSLError::SSL_ERROR;
   }
+
+  sslInternalErrorCode = errError;
 }
 
 SSLException::SSLException(SSLError error)
     : AsyncSocketException(
           exTypefromSSLErr(error), getSSLErrorString(error).str(), 0),
-      sslError(error) {}
+      sslError(error),
+      sslInternalErrorCode(0) {}
 } // namespace folly
diff --git a/folly/folly/io/async/ssl/SSLErrors.h b/folly/folly/io/async/ssl/SSLErrors.h
--- a/folly/folly/io/async/ssl/SSLErrors.h
+++ b/folly/folly/io/async/ssl/SSLErrors.h
@@ -41,7 +41,10 @@
 
   SSLError getSSLError() const { return sslError; }
 
+  unsigned long getInternalSSLError() const { return sslInternalErrorCode; }
+
  private:
   SSLError sslError;
+  unsigned long sslInternalErrorCode;
 };
 } // namespace folly
diff --git a/folly/folly/io/async/test/AsyncSSLSocketTest.h b/folly/folly/io/async/test/AsyncSSLSocketTest.h
--- a/folly/folly/io/async/test/AsyncSSLSocketTest.h
+++ b/folly/folly/io/async/test/AsyncSSLSocketTest.h
@@ -496,10 +496,15 @@
 class MockCertificateIdentityVerifier : public CertificateIdentityVerifier {
  public:
   MOCK_METHOD(
+      bool,
+      verifyContext,
+      (bool, X509_STORE_CTX*),
+      (const, noexcept, override));
+  MOCK_METHOD(
       std::unique_ptr<AsyncTransportCertificate>,
       verifyLeaf,
       (const AsyncTransportCertificate&),
-      (const));
+      (const, override));
 };
 
 class MockHandshakeCB : public AsyncSSLSocket::HandshakeCB {
@@ -706,7 +711,7 @@
 
     hcb_->setSocket(sock);
     sock->getEventBase()->tryRunAfterDelay(
-        [=] {
+        [=, this] {
           std::cerr << "Delayed SSL accept, client will have close by now"
                     << std::endl;
           // SSL accept will fail
diff --git a/folly/folly/io/async/test/AsyncSocketTest.h b/folly/folly/io/async/test/AsyncSocketTest.h
--- a/folly/folly/io/async/test/AsyncSocketTest.h
+++ b/folly/folly/io/async/test/AsyncSocketTest.h
@@ -70,6 +70,7 @@
   void releaseIOBuf(std::unique_ptr<folly::IOBuf> ioBuf) noexcept override {
     numIoBufCount += ioBuf->countChainElements();
     numIoBufBytes += ioBuf->computeChainDataLength();
+    releaseIOBufCallbackCalled = true;
   }
 
   StateEnum state;
@@ -81,6 +82,7 @@
   VoidCallback errorCallback;
   ReleaseIOBufCallback* releaseIOBufCallback;
   size_t writeStartingInvocations{0};
+  bool releaseIOBufCallbackCalled{false};
 };
 
 class ReadCallback : public folly::AsyncTransport::ReadCallback {
@@ -108,13 +110,32 @@
 
   void readDataAvailable(size_t len) noexcept override {
     currentBuffer.length = len;
-    buffers.push_back(currentBuffer);
+    buffers.push_back(std::move(currentBuffer));
     currentBuffer.reset();
     if (dataAvailableCallback) {
       dataAvailableCallback();
     }
   }
 
+  size_t maxBufferSize() const override { return maxBufferSz; }
+  bool isBufferMovable() noexcept override { return true; }
+  void readBufferAvailable(std::unique_ptr<IOBuf> readBuf) noexcept override {
+    if (currentBuffer.buffer && currentBuffer.length > 0) {
+      buffers.push_back(std::move(currentBuffer));
+    }
+    currentBuffer.reset();
+
+    auto coalesced = readBuf->cloneCoalesced();
+    currentBuffer.buffer = (char*)coalesced->writableData();
+    currentBuffer.length = coalesced->length();
+    currentBuffer.ioBuf = std::move(coalesced);
+    buffers.push_back(std::move(currentBuffer));
+    currentBuffer.reset();
+    if (dataAvailableCallback) {
+      dataAvailableCallback();
+    }
+  }
+
   void readEOF() noexcept override { state = STATE_SUCCEEDED; }
 
   void readErr(const folly::AsyncSocketException& ex) noexcept override {
@@ -153,14 +174,42 @@
     return ret;
   }
 
+  void coalesce() {
+    if (buffers.empty()) {
+      return;
+    }
+
+    std::unique_ptr<IOBuf> coalesced;
+
+    for (auto& buf : buffers) {
+      if (!buf.ioBuf) {
+        return;
+      }
+
+      if (!coalesced) {
+        coalesced = std::move(buf.ioBuf);
+      } else {
+        coalesced->appendToChain(std::move(buf.ioBuf));
+      }
+    }
+    buffers.clear();
+    coalesced->coalesce();
+    buffers.emplace_back(std::move(coalesced));
+  }
+
   class Buffer {
    public:
     Buffer() : buffer(nullptr), length(0) {}
     Buffer(char* buf, size_t len) : buffer(buf), length(len) {}
+    explicit Buffer(std::unique_ptr<IOBuf> ioBuf)
+        : buffer((char*)ioBuf->writableData()),
+          length(ioBuf->length()),
+          ioBuf(std::move(ioBuf)) {}
 
     void reset() {
       buffer = nullptr;
       length = 0;
+      ioBuf.reset();
     }
     void allocate(size_t len) {
       assert(buffer == nullptr);
@@ -168,12 +217,15 @@
       this->length = len;
     }
     void free() {
-      ::free(buffer);
+      if (!ioBuf) {
+        ::free(buffer);
+      }
       reset();
     }
 
     char* buffer;
     size_t length;
+    std::unique_ptr<IOBuf> ioBuf;
   };
 
   StateEnum state;
@@ -255,6 +307,15 @@
     }
   }
 
+  bool isBufferMovable() noexcept override { return true; }
+  void readBufferAvailable(std::unique_ptr<IOBuf> readBuf) noexcept override {
+    if (!buf_) {
+      buf_ = std::move(readBuf);
+    } else {
+      buf_->appendToChain(std::move(readBuf));
+    }
+  }
+
   void reset() { buf_.reset(); }
 
   void readEOF() noexcept override { state_ = STATE_SUCCEEDED; }
@@ -388,6 +449,15 @@
     }
   }
 
+  bool isBufferMovable() noexcept override { return true; }
+  void readBufferAvailable(std::unique_ptr<IOBuf> readBuf) noexcept override {
+    if (!data_) {
+      data_ = std::move(readBuf);
+    } else {
+      data_->prependChain(std::move(readBuf));
+    }
+  }
+
   void readEOF() noexcept override { state = STATE_SUCCEEDED; }
 
   void readErr(const folly::AsyncSocketException& ex) noexcept override {
@@ -556,7 +626,8 @@
     }
     if (enableTFO) {
 #if FOLLY_ALLOW_TFO
-      folly::detail::tfo_enable(fd_, 100);
+      auto ret = folly::detail::tfo_enable(fd_, 100);
+      PCHECK(ret == 0);
 #endif
     }
 
diff --git a/folly/folly/io/async/test/AsyncSocketTest2.h b/folly/folly/io/async/test/AsyncSocketTest2.h
--- a/folly/folly/io/async/test/AsyncSocketTest2.h
+++ b/folly/folly/io/async/test/AsyncSocketTest2.h
@@ -156,7 +156,7 @@
     folly::SocketAddress address; // valid for TYPE_ACCEPT
     std::string errorMsg; // valid for TYPE_ERROR
   };
-  typedef std::deque<EventInfo> EventList;
+  using EventList = std::deque<EventInfo>;
 
   TestAcceptCallback()
       : connectionAcceptedFn_(),
@@ -231,6 +231,24 @@
   }
   void connectSuccess() noexcept override {}
   void connectErr(const AsyncSocketException& /*ex*/) noexcept override {}
+};
+
+class TestPortAssignmentCallback : public AsyncSocket::ConnectCallback {
+ public:
+  void preConnect(NetworkSocket fd) noexcept override {
+    // Get the local address after binding but before connecting
+    SocketAddress localAddr;
+    sockaddr_storage storage{};
+    socklen_t addrLen = sizeof(storage);
+    if (netops::getsockname(
+            fd, reinterpret_cast<sockaddr*>(&storage), &addrLen) == 0) {
+      localAddr.setFromSockaddr(reinterpret_cast<sockaddr*>(&storage), addrLen);
+      assignedPort = localAddr.getPort();
+    }
+  }
+  void connectSuccess() noexcept override {}
+  void connectErr(const AsyncSocketException& /*ex*/) noexcept override {}
+  uint16_t assignedPort = 0;
 };
 
 } // namespace test
diff --git a/folly/folly/io/async/test/MockAsyncServerSocket.h b/folly/folly/io/async/test/MockAsyncServerSocket.h
--- a/folly/folly/io/async/test/MockAsyncServerSocket.h
+++ b/folly/folly/io/async/test/MockAsyncServerSocket.h
@@ -26,7 +26,7 @@
 
 class MockAsyncServerSocket : public AsyncServerSocket {
  public:
-  typedef std::unique_ptr<MockAsyncServerSocket, Destructor> UniquePtr;
+  using UniquePtr = std::unique_ptr<MockAsyncServerSocket, Destructor>;
 
   // We explicitly do not mock destroy(), since the base class implementation
   // in DelayedDestruction is what actually deletes the object.
diff --git a/folly/folly/io/async/test/MockAsyncSocket.h b/folly/folly/io/async/test/MockAsyncSocket.h
--- a/folly/folly/io/async/test/MockAsyncSocket.h
+++ b/folly/folly/io/async/test/MockAsyncSocket.h
@@ -26,7 +26,7 @@
 
 class MockAsyncSocket : public AsyncSocket {
  public:
-  typedef std::unique_ptr<MockAsyncSocket, Destructor> UniquePtr;
+  using UniquePtr = std::unique_ptr<MockAsyncSocket, Destructor>;
 
   explicit MockAsyncSocket(EventBase* base) : AsyncSocket(base) {}
 
diff --git a/folly/folly/io/async/test/MockAsyncTransport.h b/folly/folly/io/async/test/MockAsyncTransport.h
--- a/folly/folly/io/async/test/MockAsyncTransport.h
+++ b/folly/folly/io/async/test/MockAsyncTransport.h
@@ -26,7 +26,6 @@
 
 class MockAsyncTransport : public AsyncTransport {
  public:
-  MOCK_METHOD(void, setEventCallback, (EventRecvmsgCallback*));
   MOCK_METHOD(void, setReadCB, (ReadCallback*));
   MOCK_METHOD(ReadCallback*, getReadCallback, (), (const));
   MOCK_METHOD(ReadCallback*, getReadCB, (), (const));
diff --git a/folly/folly/io/async/test/ScopedBoundPort.cpp b/folly/folly/io/async/test/ScopedBoundPort.cpp
deleted file mode 100644
--- a/folly/folly/io/async/test/ScopedBoundPort.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/io/async/test/ScopedBoundPort.h>
-
-#include <folly/Memory.h>
-#include <folly/io/async/AsyncServerSocket.h>
-#include <folly/io/async/ScopedEventBaseThread.h>
-
-namespace folly {
-
-ScopedBoundPort::ScopedBoundPort(IPAddress host) {
-  ebth_ = std::make_unique<ScopedEventBaseThread>();
-  ebth_->getEventBase()->runInEventBaseThreadAndWait([&] {
-    sock_ = AsyncServerSocket::newSocket(ebth_->getEventBase());
-    sock_->bind(SocketAddress(host, 0));
-  });
-}
-
-ScopedBoundPort::~ScopedBoundPort() {
-  ebth_->getEventBase()->runInEventBaseThread([sock = std::move(sock_)] {});
-}
-
-SocketAddress ScopedBoundPort::getAddress() const {
-  return sock_->getAddress();
-}
-} // namespace folly
diff --git a/folly/folly/io/async/test/SocketPair.cpp b/folly/folly/io/async/test/SocketPair.cpp
deleted file mode 100644
--- a/folly/folly/io/async/test/SocketPair.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/io/async/test/SocketPair.h>
-
-#include <cerrno>
-#include <stdexcept>
-
-#include <folly/Conv.h>
-#include <folly/net/NetOps.h>
-#include <folly/portability/Fcntl.h>
-#include <folly/portability/Sockets.h>
-#include <folly/portability/Unistd.h>
-
-namespace folly {
-
-SocketPair::SocketPair(Mode mode) {
-  if (netops::socketpair(PF_UNIX, SOCK_STREAM, 0, fds_) != 0) {
-    throw std::runtime_error(folly::to<std::string>(
-        "test::SocketPair: failed create socket pair", errno));
-  }
-
-  if (mode == NONBLOCKING) {
-    if (netops::set_socket_non_blocking(fds_[0]) != 0) {
-      throw std::runtime_error(folly::to<std::string>(
-          "test::SocketPair: failed to set non-blocking "
-          "read mode",
-          errno));
-    }
-    if (netops::set_socket_non_blocking(fds_[1]) != 0) {
-      throw std::runtime_error(folly::to<std::string>(
-          "test::SocketPair: failed to set non-blocking "
-          "write mode",
-          errno));
-    }
-  }
-}
-
-SocketPair::~SocketPair() {
-  closeFD0();
-  closeFD1();
-}
-
-void SocketPair::closeFD0() {
-  if (fds_[0] != NetworkSocket()) {
-    netops::close(fds_[0]);
-    fds_[0] = NetworkSocket();
-  }
-}
-
-void SocketPair::closeFD1() {
-  if (fds_[1] != NetworkSocket()) {
-    netops::close(fds_[1]);
-    fds_[1] = NetworkSocket();
-  }
-}
-
-} // namespace folly
diff --git a/folly/folly/io/async/test/TimeUtil.cpp b/folly/folly/io/async/test/TimeUtil.cpp
deleted file mode 100644
--- a/folly/folly/io/async/test/TimeUtil.cpp
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __STDC_FORMAT_MACROS
-#define __STDC_FORMAT_MACROS
-#endif
-
-#include <folly/io/async/test/TimeUtil.h>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <cerrno>
-#ifdef __linux__
-#include <sys/utsname.h>
-#endif
-
-#include <chrono>
-#include <ostream>
-#include <stdexcept>
-
-#include <glog/logging.h>
-
-#include <folly/Conv.h>
-#include <folly/Portability.h>
-#include <folly/ScopeGuard.h>
-#include <folly/String.h>
-#include <folly/portability/Unistd.h>
-#include <folly/system/ThreadId.h>
-
-using std::string;
-using namespace std::chrono;
-
-namespace folly {
-
-#ifdef __linux__
-static int getLinuxVersion(StringPiece release) {
-  const auto dot1 = release.find('.');
-  if (dot1 == StringPiece::npos) {
-    throw std::invalid_argument("could not find first dot");
-  }
-  const auto v1 = folly::to<int>(release.subpiece(0, dot1));
-
-  const auto dot2 = release.find('.', dot1 + 1);
-  if (dot2 == StringPiece::npos) {
-    throw std::invalid_argument("could not find second dot");
-  }
-  const auto v2 = folly::to<int>(release.subpiece(dot1 + 1, dot2 - (dot1 + 1)));
-
-  const auto dash = release.find('-', dot2 + 1);
-  const auto v3 = folly::to<int>(release.subpiece(dot2 + 1, dash - (dot2 + 1)));
-
-  return ((v1 * 1000 + v2) * 1000) + v3;
-}
-
-/**
- * Determine the time units used in /proc/<pid>/schedstat
- *
- * Returns the number of nanoseconds per time unit,
- * or -1 if we cannot determine the units.
- */
-static int64_t determineSchedstatUnits() {
-  struct utsname unameInfo;
-  if (uname(&unameInfo) != 0) {
-    LOG(ERROR) << "unable to determine jiffies/second: uname failed: %s"
-               << errnoStr(errno);
-    return -1;
-  }
-
-  // In Linux version 2.6.23 and later, time time values are always
-  // reported in nanoseconds.
-  //
-  // This change appears to have been made in commit 425e0968a25f, which
-  // moved some of the sched stats code to a new file.  Despite the commit
-  // message claiming "no code changes are caused by this patch", it changed
-  // the task.sched_info.cpu_time and task.sched_info.run_delay counters to be
-  // computed using sched_clock() rather than jiffies.
-  int linuxVersion;
-  try {
-    linuxVersion = getLinuxVersion(unameInfo.release);
-  } catch (const std::exception&) {
-    LOG(ERROR) << "unable to determine jiffies/second: failed to parse "
-               << "kernel release string \"" << unameInfo.release << "\"";
-    return -1;
-  }
-  if (linuxVersion >= 2006023) {
-    // The units are nanoseconds
-    return 1;
-  }
-
-  // In Linux versions prior to 2.6.23, the time values are reported in
-  // jiffies.  This is somewhat unfortunate, as the number of jiffies per
-  // second is configurable.  We have to determine the units being used.
-  //
-  // It seems like the only real way to figure out the CONFIG_HZ value used by
-  // this kernel is to look it up in the config file.
-  //
-  // Look in /boot/config-<kernel_release>
-  char configPath[256];
-  snprintf(
-      configPath, sizeof(configPath), "/boot/config-%s", unameInfo.release);
-
-  FILE* f = fopen(configPath, "r");
-  if (f == nullptr) {
-    LOG(ERROR) << "unable to determine jiffies/second: "
-                  "cannot open kernel config file %s"
-               << configPath;
-    return -1;
-  }
-  SCOPE_EXIT {
-    fclose(f);
-  };
-
-  int64_t hz = -1;
-  char buf[1024];
-  while (fgets(buf, sizeof(buf), f) != nullptr) {
-    if (strcmp(buf, "CONFIG_NO_HZ=y\n") == 0) {
-      LOG(ERROR) << "unable to determine jiffies/second: tickless kernel";
-      return -1;
-    } else if (strcmp(buf, "CONFIG_HZ=1000\n") == 0) {
-      hz = 1000;
-    } else if (strcmp(buf, "CONFIG_HZ=300\n") == 0) {
-      hz = 300;
-    } else if (strcmp(buf, "CONFIG_HZ=250\n") == 0) {
-      hz = 250;
-    } else if (strcmp(buf, "CONFIG_HZ=100\n") == 0) {
-      hz = 100;
-    }
-  }
-
-  if (hz == -1) {
-    LOG(ERROR) << "unable to determine jiffies/second: no CONFIG_HZ setting "
-                  "found in %s"
-               << configPath;
-    return -1;
-  }
-
-  return hz;
-}
-#endif
-
-/**
- * Determine how long this process has spent waiting to get scheduled on the
- * CPU.
- *
- * Returns the number of nanoseconds spent waiting, or -1 if the amount of
- * time cannot be determined.
- */
-static nanoseconds getSchedTimeWaiting(pid_t tid) {
-#ifndef __linux__
-  (void)tid;
-  return nanoseconds(0);
-#else
-  static int64_t timeUnits = determineSchedstatUnits();
-  if (timeUnits < 0) {
-    // We couldn't figure out how many jiffies there are in a second.
-    // Don't bother reading the schedstat info if we can't interpret it.
-    return nanoseconds(0);
-  }
-
-  int fd = -1;
-  try {
-    char schedstatFile[256];
-    snprintf(schedstatFile, sizeof(schedstatFile), "/proc/%d/schedstat", tid);
-    fd = open(schedstatFile, O_RDONLY);
-    if (fd < 0) {
-      throw std::runtime_error(
-          folly::to<string>("failed to open process schedstat file", errno));
-    }
-
-    char buf[512];
-    ssize_t bytesReadRet = fileops::read(fd, buf, sizeof(buf) - 1);
-    if (bytesReadRet <= 0) {
-      throw std::runtime_error(
-          folly::to<string>("failed to read process schedstat file", errno));
-    }
-    auto bytesRead = size_t(bytesReadRet);
-
-    if (buf[bytesRead - 1] != '\n') {
-      throw std::runtime_error("expected newline at end of schedstat data");
-    }
-    assert(bytesRead < sizeof(buf));
-    buf[bytesRead] = '\0';
-
-    uint64_t activeJiffies = 0;
-    uint64_t waitingJiffies = 0;
-    uint64_t numTasks = 0;
-    int rc = sscanf(
-        buf,
-        "%" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
-        &activeJiffies,
-        &waitingJiffies,
-        &numTasks);
-    if (rc != 3) {
-      throw std::runtime_error("failed to parse schedstat data");
-    }
-
-    fileops::close(fd);
-    return nanoseconds(waitingJiffies * timeUnits);
-  } catch (const std::runtime_error& e) {
-    if (fd >= 0) {
-      fileops::close(fd);
-    }
-    LOG(ERROR) << "error determining process wait time: %s" << e.what();
-    return nanoseconds(0);
-  }
-#endif
-}
-
-void TimePoint::reset() {
-  // Remember the current time
-  timeStart_ = steady_clock::now();
-
-  // Remember how long this process has spent waiting to be scheduled
-  tid_ = getOSThreadID();
-  timeWaiting_ = getSchedTimeWaiting(tid_);
-
-  // In case it took a while to read the schedstat info,
-  // also record the time after the schedstat check
-  timeEnd_ = steady_clock::now();
-}
-
-std::ostream& operator<<(std::ostream& os, const TimePoint& timePoint) {
-  os << "TimePoint(" << timePoint.getTimeStart().time_since_epoch().count()
-     << ", " << timePoint.getTimeEnd().time_since_epoch().count() << ", "
-     << timePoint.getTimeWaiting().count() << ")";
-  return os;
-}
-
-bool checkTimeout(
-    const TimePoint& start,
-    const TimePoint& end,
-    nanoseconds expected,
-    bool allowSmaller,
-    nanoseconds tolerance) {
-  auto elapsedTime = end.getTimeStart() - start.getTimeEnd();
-
-  if (!allowSmaller) {
-    // Timeouts should never fire before the time was up.
-    // Allow 1ms of wiggle room for rounding errors.
-    if (elapsedTime < (expected - milliseconds(1))) {
-      return false;
-    }
-  }
-
-  // Check that the event fired within a reasonable time of the timout.
-  //
-  // If the system is under heavy load, our process may have had to wait for a
-  // while to be run.  The time spent waiting for the processor shouldn't
-  // count against us, so exclude this time from the check.
-  nanoseconds timeExcluded;
-  if (end.getTid() != start.getTid()) {
-    // We can only correctly compute the amount of time waiting to be scheduled
-    // if both TimePoints were set in the same thread.
-    timeExcluded = nanoseconds(0);
-  } else {
-    timeExcluded = end.getTimeWaiting() - start.getTimeWaiting();
-    assert(end.getTimeWaiting() >= start.getTimeWaiting());
-    // Add a tolerance here due to precision issues on linux, see below note.
-    assert((elapsedTime + tolerance) >= timeExcluded);
-  }
-
-  nanoseconds effectiveElapsedTime(0);
-  if (elapsedTime > timeExcluded) {
-    effectiveElapsedTime = elapsedTime - timeExcluded;
-  }
-
-  if (!kIsLinux) {
-    // We can only compute timeExcluded accurately on Linux.
-    // On other platforms, just increase the amount of tolerance allowed to
-    // account for time possibly spent waiting to be scheduled.
-    tolerance += 20ms;
-  }
-
-  // On x86 Linux, sleep calls generally have precision only to the nearest
-  // millisecond.  The tolerance parameter lets users allow a few ms of slop.
-  auto overrun = effectiveElapsedTime - expected;
-  return overrun <= tolerance;
-}
-
-} // namespace folly
diff --git a/folly/folly/io/coro/ServerSocket.cpp b/folly/folly/io/coro/ServerSocket.cpp
--- a/folly/folly/io/coro/ServerSocket.cpp
+++ b/folly/folly/io/coro/ServerSocket.cpp
@@ -111,7 +111,7 @@
   co_await baton;
   if (cancelToken.isCancellationRequested()) {
     socket_->stopAccepting();
-    co_yield co_cancelled;
+    co_yield co_stopped_may_throw;
   }
   if (cb.error) {
     co_yield co_error(std::move(cb.error));
diff --git a/folly/folly/io/coro/ServerSocket.h b/folly/folly/io/coro/ServerSocket.h
--- a/folly/folly/io/coro/ServerSocket.h
+++ b/folly/folly/io/coro/ServerSocket.h
@@ -42,6 +42,7 @@
 
   ServerSocket(ServerSocket&&) = default;
   ServerSocket& operator=(ServerSocket&&) = default;
+  ~ServerSocket() = default;
 
   Task<std::unique_ptr<Transport>> accept();
 
diff --git a/folly/folly/io/coro/TransportCallbackBase.h b/folly/folly/io/coro/TransportCallbackBase.h
--- a/folly/folly/io/coro/TransportCallbackBase.h
+++ b/folly/folly/io/coro/TransportCallbackBase.h
@@ -45,7 +45,7 @@
     auto cancelToken = co_await co_current_cancellation_token;
     if (cancelToken.isCancellationRequested()) {
       cancel();
-      co_yield folly::coro::co_cancelled;
+      co_yield folly::coro::co_stopped_may_throw;
     }
     folly::CancellationCallback cancellationCallback{
         cancelToken, [this] {
@@ -58,7 +58,7 @@
 
     if (cancelToken.isCancellationRequested()) {
       cancel();
-      co_yield folly::coro::co_cancelled;
+      co_yield folly::coro::co_stopped_may_throw;
     }
     co_return folly::unit;
   }
diff --git a/folly/folly/json.h b/folly/folly/json.h
--- a/folly/folly/json.h
+++ b/folly/folly/json.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/json/json.h>
+#include <folly/json/json.h> // @shim
diff --git a/folly/folly/json/DynamicConverter.h b/folly/folly/json/DynamicConverter.h
--- a/folly/folly/json/DynamicConverter.h
+++ b/folly/folly/json/DynamicConverter.h
@@ -86,7 +86,7 @@
 
 template <typename T>
 struct iterator_class_is_container {
-  typedef typename T::iterator some_iterator;
+  using some_iterator = typename T::iterator;
   enum {
     value = is_detected_v<detect_member_type_value_type, T> &&
         std::is_constructible<T, some_iterator, some_iterator>::value
@@ -179,7 +179,7 @@
           iterator_adaptor<Transformer<T, It>, It, typename T::value_type> {
   friend class boost::iterator_core_access;
 
-  typedef typename T::value_type ttype;
+  using ttype = typename T::value_type;
 
   mutable Optional<ttype> cache_;
 
diff --git a/folly/folly/json/DynamicParser-inl.h b/folly/folly/json/DynamicParser-inl.h
--- a/folly/folly/json/DynamicParser-inl.h
+++ b/folly/folly/json/DynamicParser-inl.h
@@ -246,10 +246,11 @@
   wrapError(&key, [&]() {
     auto vp = value().get_ptr(key);
     if (!vp) {
-      throw std::runtime_error(folly::to<std::string>(
-          "Couldn't find key ",
-          detail::toPseudoJson(key),
-          " in dynamic object"));
+      throw std::runtime_error(
+          folly::to<std::string>(
+              "Couldn't find key ",
+              detail::toPseudoJson(key),
+              " in dynamic object"));
     }
     parse(key, *vp, fn);
   });
diff --git a/folly/folly/json/DynamicParser.h b/folly/folly/json/DynamicParser.h
--- a/folly/folly/json/DynamicParser.h
+++ b/folly/folly/json/DynamicParser.h
@@ -189,8 +189,9 @@
  */
 struct FOLLY_EXPORT DynamicParserParseError : public std::runtime_error {
   explicit DynamicParserParseError(folly::dynamic error)
-      : std::runtime_error(folly::to<std::string>(
-            "DynamicParserParseError: ", detail::toPseudoJson(error))),
+      : std::runtime_error(
+            folly::to<std::string>(
+                "DynamicParserParseError: ", detail::toPseudoJson(error))),
         error_(std::move(error)) {}
   /**
    * Structured just like releaseErrors(), but with only 1 error inside:
diff --git a/folly/folly/json/JSONSchema.cpp b/folly/folly/json/JSONSchema.cpp
--- a/folly/folly/json/JSONSchema.cpp
+++ b/folly/folly/json/JSONSchema.cpp
@@ -16,7 +16,6 @@
 
 #include <folly/json/JSONSchema.h>
 
-#include <boost/algorithm/string/replace.hpp>
 #include <boost/regex.hpp>
 
 #include <folly/CPortability.h>
@@ -33,6 +32,14 @@
 
 namespace {
 
+void replaceAll(std::string& s, StringPiece from, StringPiece to) {
+  size_t pos = 0;
+  while ((pos = s.find(from.data(), pos, from.size())) != std::string::npos) {
+    s.replace(pos, from.size(), to.data(), to.size());
+    pos += to.size();
+  }
+}
+
 /**
  * We throw this exception when schema validation fails.
  */
@@ -41,16 +48,18 @@
   SchemaError(const SchemaError&) = default;
 
   SchemaError(folly::StringPiece expected, const dynamic& value)
-      : std::runtime_error(to<std::string>(
-            "Expected to get ", expected, " for value ", toJson(value))) {}
+      : std::runtime_error(
+            to<std::string>(
+                "Expected to get ", expected, " for value ", toJson(value))) {}
   SchemaError(
       folly::StringPiece expected, const dynamic& schema, const dynamic& value)
-      : std::runtime_error(to<std::string>(
-            "Expected to get ",
-            expected,
-            toJson(schema),
-            " for value ",
-            toJson(value))) {}
+      : std::runtime_error(
+            to<std::string>(
+                "Expected to get ",
+                expected,
+                toJson(schema),
+                " for value ",
+                toJson(value))) {}
 };
 
 template <class... Args>
@@ -677,12 +686,12 @@
       // This is a ref, but we haven't loaded it yet. Find where it is based on
       // the root schema.
       std::vector<std::string> parts;
-      split("/", p->stringPiece(), parts);
+      split('/', p->stringPiece(), parts);
       const auto* s = &context.schema; // First part is '#'
       for (size_t i = 1; s && i < parts.size(); ++i) {
         // Per the standard, we must replace ~1 with / and then ~0 with ~
-        boost::replace_all(parts[i], "~1", "/");
-        boost::replace_all(parts[i], "~0", "~");
+        replaceAll(parts[i], "~1", "/");
+        replaceAll(parts[i], "~0", "~");
         if (s->isObject()) {
           s = s->get_ptr(parts[i]);
           continue;
@@ -721,16 +730,18 @@
     validators_.emplace_back(std::make_unique<MultipleOfValidator>(*p));
   }
   if (const auto* p = schema.get_ptr("maximum")) {
-    validators_.emplace_back(std::make_unique<ComparisonValidator>(
-        *p,
-        schema.get_ptr("exclusiveMaximum"),
-        ComparisonValidator::Type::MAX));
+    validators_.emplace_back(
+        std::make_unique<ComparisonValidator>(
+            *p,
+            schema.get_ptr("exclusiveMaximum"),
+            ComparisonValidator::Type::MAX));
   }
   if (const auto* p = schema.get_ptr("minimum")) {
-    validators_.emplace_back(std::make_unique<ComparisonValidator>(
-        *p,
-        schema.get_ptr("exclusiveMinimum"),
-        ComparisonValidator::Type::MIN));
+    validators_.emplace_back(
+        std::make_unique<ComparisonValidator>(
+            *p,
+            schema.get_ptr("exclusiveMinimum"),
+            ComparisonValidator::Type::MIN));
   }
 
   // String validators
@@ -774,8 +785,9 @@
   const auto* patternProperties = schema.get_ptr("patternProperties");
   const auto* additionalProperties = schema.get_ptr("additionalProperties");
   if (properties || patternProperties || additionalProperties) {
-    validators_.emplace_back(std::make_unique<PropertiesValidator>(
-        context, properties, patternProperties, additionalProperties));
+    validators_.emplace_back(
+        std::make_unique<PropertiesValidator>(
+            context, properties, patternProperties, additionalProperties));
   }
   if (const auto* p = schema.get_ptr("maxProperties")) {
     validators_.emplace_back(
@@ -806,12 +818,14 @@
     validators_.emplace_back(std::make_unique<AllOfValidator>(context, *p));
   }
   if (const auto* p = schema.get_ptr("anyOf")) {
-    validators_.emplace_back(std::make_unique<AnyOfValidator>(
-        context, *p, AnyOfValidator::Type::ONE_OR_MORE));
+    validators_.emplace_back(
+        std::make_unique<AnyOfValidator>(
+            context, *p, AnyOfValidator::Type::ONE_OR_MORE));
   }
   if (const auto* p = schema.get_ptr("oneOf")) {
-    validators_.emplace_back(std::make_unique<AnyOfValidator>(
-        context, *p, AnyOfValidator::Type::EXACTLY_ONE));
+    validators_.emplace_back(
+        std::make_unique<AnyOfValidator>(
+            context, *p, AnyOfValidator::Type::EXACTLY_ONE));
   }
   if (const auto* p = schema.get_ptr("not")) {
     validators_.emplace_back(std::make_unique<NotValidator>(context, *p));
diff --git a/folly/folly/json/bser/Dump.cpp b/folly/folly/json/bser/Dump.cpp
--- a/folly/folly/json/bser/Dump.cpp
+++ b/folly/folly/json/bser/Dump.cpp
@@ -207,8 +207,11 @@
   // compute the length
   auto len = q.chainLength();
   if (len > uint64_t(std::numeric_limits<int64_t>::max())) {
-    throw std::range_error(folly::to<std::string>(
-        "serialized data size ", len, " is too large to represent as BSER"));
+    throw std::range_error(
+        folly::to<std::string>(
+            "serialized data size ",
+            len,
+            " is too large to represent as BSER"));
   }
 
   // This is a bit verbose, but it computes a header that is appropriate
diff --git a/folly/folly/json/bser/Load.cpp b/folly/folly/json/bser/Load.cpp
--- a/folly/folly/json/bser/Load.cpp
+++ b/folly/folly/json/bser/Load.cpp
@@ -27,11 +27,12 @@
 
 template <typename... ARGS>
 [[noreturn]] static void throwDecodeError(Cursor& curs, ARGS&&... args) {
-  throw BserDecodeError(folly::to<std::string>(
-      std::forward<ARGS>(args)...,
-      " with ",
-      curs.length(),
-      " bytes remaining in cursor"));
+  throw BserDecodeError(
+      folly::to<std::string>(
+          std::forward<ARGS>(args)...,
+          " with ",
+          curs.length(),
+          " bytes remaining in cursor"));
 }
 
 static int64_t decodeInt(Cursor& curs) {
diff --git a/folly/folly/json/dynamic-inl.h b/folly/folly/json/dynamic-inl.h
--- a/folly/folly/json/dynamic-inl.h
+++ b/folly/folly/json/dynamic-inl.h
@@ -822,13 +822,38 @@
 template <typename K>
 dynamic::IfIsNonStringDynamicConvertible<K, std::size_t> dynamic::count(
     K&& key) const {
+  if (const auto* as_array = get_nothrow<Array>()) {
+    return std::count(as_array->begin(), as_array->end(), std::forward<K>(key));
+  }
   return find(std::forward<K>(key)) != items().end() ? 1u : 0u;
 }
 
 inline std::size_t dynamic::count(StringPiece key) const {
+  if (const auto* as_array = get_nothrow<Array>()) {
+    return std::count(as_array->begin(), as_array->end(), key);
+  }
   return find(key) != items().end() ? 1u : 0u;
 }
 
+template <typename K>
+dynamic::IfIsNonStringDynamicConvertible<K, bool> dynamic::contains(
+    K&& key) const {
+  if (const auto* as_array = get_nothrow<Array>()) {
+    return std::find(
+               as_array->begin(), as_array->end(), std::forward<K>(key)) !=
+        as_array->end();
+  }
+  return find(std::forward<K>(key)) != items().end();
+}
+
+inline bool dynamic::contains(StringPiece key) const {
+  if (const auto* as_array = get_nothrow<Array>()) {
+    return std::find(as_array->begin(), as_array->end(), key) !=
+        as_array->end();
+  }
+  return find(key) != items().end();
+}
+
 template <class K, class V>
 inline dynamic::IfNotIterator<K, void> dynamic::insert(K&& key, V&& val) {
   auto& obj = get<ObjectImpl>();
@@ -1156,6 +1181,40 @@
 inline std::ostream& operator<<(std::ostream& out, dynamic const& d) {
   d.print(out);
   return out;
+}
+
+template <typename Val>
+size_t erase(folly::dynamic& dyn, Val const& val) {
+  return erase_if(dyn, [&](dynamic const& d) { return d == val; });
+}
+
+template <typename Pred>
+size_t erase_if(dynamic& dyn, Pred pred) {
+  if (dyn.isArray()) {
+    using item_type = dynamic;
+    if constexpr (std::is_invocable_v<Pred&, item_type const&>) {
+      auto b = dyn.begin();
+      auto e = dyn.end();
+      auto f = std::remove_if(b, e, std::ref(pred));
+      auto c = e - f;
+      dyn.erase(f, e);
+      return c;
+    }
+  }
+  if (dyn.isObject()) {
+    using item_type = std::pair<dynamic const, dynamic>;
+    if constexpr (std::is_invocable_v<Pred&, item_type const&>) {
+      size_t c = 0;
+      auto view = dyn.items();
+      auto b = view.begin();
+      auto e = view.end();
+      while (b != e) {
+        b = pred(std::as_const(*b)) ? (++c, dyn.erase(b)) : std::next(b);
+      }
+      return c;
+    }
+  }
+  throw_exception<TypeError>("container", dyn.type());
 }
 
 //////////////////////////////////////////////////////////////////////
diff --git a/folly/folly/json/dynamic.h b/folly/folly/json/dynamic.h
--- a/folly/folly/json/dynamic.h
+++ b/folly/folly/json/dynamic.h
@@ -114,7 +114,7 @@
    * Object item iterators dereference as pairs of (key, value).
    */
  private:
-  typedef std::vector<dynamic> Array;
+  using Array = std::vector<dynamic>;
 
   /*
    * Violating spec, std::vector<bool>::const_reference is not bool in libcpp:
@@ -130,9 +130,9 @@
       std::vector<bool>::const_reference>;
 
  public:
-  typedef Array::iterator iterator;
-  typedef Array::const_iterator const_iterator;
-  typedef dynamic value_type;
+  using iterator = Array::iterator;
+  using const_iterator = Array::const_iterator;
+  using value_type = dynamic;
 
   struct const_key_iterator;
   struct const_value_iterator;
@@ -642,7 +642,10 @@
    * Count by key.
    *
    * If this is an object, returns whether it contains a field with
-   * the given name.  Otherwise throws TypeError.
+   * the given name.
+   * If this is an array, returns the number of elements matching
+   * the supplied value.
+   * Otherwise throws TypeError.
    *
    * @methodset Object
    */
@@ -650,6 +653,19 @@
   IfIsNonStringDynamicConvertible<K, std::size_t> count(K&&) const;
   std::size_t count(StringPiece) const;
 
+  /**
+   * Check if key exists.
+   *
+   * If this is an object, returns whether it contains a field with
+   * the given name.
+   * If this is an array, returns whether it contains a value matching
+   * the supplied value.
+   * Otherwise throws TypeError.
+   */
+  template <typename K>
+  IfIsNonStringDynamicConvertible<K, bool> contains(K&&) const;
+  bool contains(StringPiece) const;
+
  private:
   dynamic const& atImpl(dynamic const&) const&;
 
@@ -1162,6 +1178,24 @@
     aligned_storage_for_t<F14NodeMap<int, int>> objectBuffer;
   } u_;
 };
+
+/**
+ * Like erase(vector), since C++20.
+ */
+template <typename Val>
+size_t erase(folly::dynamic& dyn, Val const& val);
+
+/**
+ * Like erase_if(vector), erase_if(unordered_map), since C++20.
+ *
+ * If the predicate `pred` is formally invocable with `dynamic const&` then it
+ * must type-check when so invoked. If the predicate is formally invocable with
+ * `std::pair<dynamic const, dynamic> const&` then it must type-check when so
+ * invoked. A lambda taking `const auto&` may be at risk of violating this rule,
+ * so consider a lambda taking one of the two types explicitly, sans deduction.
+ */
+template <typename Pred>
+size_t erase_if(folly::dynamic& dyn, Pred pred);
 
 //////////////////////////////////////////////////////////////////////
 
diff --git a/folly/folly/json/json.cpp b/folly/folly/json/json.cpp
--- a/folly/folly/json/json.cpp
+++ b/folly/folly/json/json.cpp
@@ -22,7 +22,6 @@
 #include <sstream>
 #include <type_traits>
 
-#include <boost/algorithm/string.hpp>
 #include <glog/logging.h>
 
 #include <folly/Conv.h>
@@ -45,12 +44,13 @@
     unsigned int line,
     std::string const& context,
     std::string const& expected) {
-  return parse_error(to<std::string>(
-      "json parse error on line ",
-      line,
-      !context.empty() ? to<std::string>(" near `", context, '\'') : "",
-      ": ",
-      expected));
+  return parse_error(
+      to<std::string>(
+          "json parse error on line ",
+          line,
+          !context.empty() ? to<std::string>(" near `", context, '\'') : "",
+          ": ",
+          expected));
 }
 
 struct Printer {
@@ -162,10 +162,11 @@
     if (opts_.convert_int_keys && p.first.isInt()) {
       auto strKey = p.first.asString();
       if (o.count(strKey)) {
-        throw json::print_error(folly::to<std::string>(
-            "folly::toJson: Source object has integer and string keys "
-            "representing the same value: ",
-            p.first.asInt()));
+        throw json::print_error(
+            folly::to<std::string>(
+                "folly::toJson: Source object has integer and string keys "
+                "representing the same value: ",
+                p.first.asInt()));
       }
       (*this)(p.first.asString(), Context(context, p.first, true));
     } else if (!opts_.allow_non_string_keys && !p.first.isString()) {
@@ -394,7 +395,7 @@
   }
 
   bool consume(StringPiece str) {
-    if (boost::starts_with(range_, str)) {
+    if (range_.startsWith(str)) {
       range_.advance(str.size());
       storeCurrent();
       return true;
@@ -925,8 +926,9 @@
         // To escape a code point that is not in the Basic Multilingual Plane,
         // the character may be represented as a twelve-character sequence,
         // encoding the UTF-16 surrogate pair corresponding to the code point.
-        writeHex(static_cast<char16_t>(
-            0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu)));
+        writeHex(
+            static_cast<char16_t>(
+                0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu)));
         writeHex(static_cast<char16_t>(0xdc00u + ((cp - 0x10000u) & 0x3ffu)));
       }
     } else if (*p == '\\' || *p == '\"') {
diff --git a/folly/folly/json/json_pointer.cpp b/folly/folly/json/json_pointer.cpp
--- a/folly/folly/json/json_pointer.cpp
+++ b/folly/folly/json/json_pointer.cpp
@@ -33,7 +33,7 @@
   }
 
   std::vector<std::string> tokens;
-  splitTo<std::string>("/", str, std::inserter(tokens, tokens.begin()));
+  splitTo<std::string>('/', str, std::inserter(tokens, tokens.begin()));
   tokens.erase(tokens.begin());
 
   for (auto& token : tokens) {
diff --git a/folly/folly/json_patch.h b/folly/folly/json_patch.h
--- a/folly/folly/json_patch.h
+++ b/folly/folly/json_patch.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/json/json_patch.h>
+#include <folly/json/json_patch.h> // @shim
diff --git a/folly/folly/json_pointer.h b/folly/folly/json_pointer.h
--- a/folly/folly/json_pointer.h
+++ b/folly/folly/json_pointer.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/json/json_pointer.h>
+#include <folly/json/json_pointer.h> // @shim
diff --git a/folly/folly/lang/Align.h b/folly/folly/lang/Align.h
--- a/folly/folly/lang/Align.h
+++ b/folly/folly/lang/Align.h
@@ -59,7 +59,7 @@
 /// register_pass_t
 ///
 /// Chooses an optimal argument type for passing values of type T based on
-/// whehter such values may be passed in registers.
+/// whether such values may be passed in registers.
 template <typename T>
 using register_pass_t = conditional_t<is_register_pass_v<T>, T const, T const&>;
 
diff --git a/folly/folly/lang/Bindings.h b/folly/folly/lang/Bindings.h
deleted file mode 100644
--- a/folly/folly/lang/Bindings.h
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/Utility.h>
-#include <folly/detail/tuple.h>
-
-FOLLY_PUSH_WARNING
-FOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS
-
-///
-/// READ ME: The docs for this library are in `Bindings.md`.
-///
-
-namespace folly::bindings {
-
-template <typename, typename... As>
-constexpr auto make_in_place(As&&...);
-template <typename... As>
-constexpr auto make_in_place_with(auto, As&&...);
-
-// The primitive for representing lists of bound args. Unary forms:
-//  - `bound_args<V>`: if `V` is already `like_bound_args`, wraps that
-//    binding, unmodified.
-//  - `bound_args<V&>` or `bound_args<V&&>`: binds a reference preserving
-//    the value category of the input.
-// Plural `bound_args<Ts...>` is a concatenation of many `like_bound_args`,
-// equivalent to `merge_and_update_bound_args` with `std::identity`.
-template <typename...>
-class bound_args;
-
-namespace detail::lite_tuple {
-using namespace folly::detail::lite_tuple;
-}
-
-namespace ext { // For extension authors -- public details
-
-// Any object modeling a list of bound args with modifiers must publicly
-// derive from `like_bound_args`, and must also implement:
-//  public:
-//   using binding_list_t = ...;
-//   constexpr auto unsafe_tuple_to_bind() && noexcept [[clang::lifetimebound]]
-//   constexpr YOUR_CLASS(bound_args_unsafe_move, tuple_to_bind_t t);
-class like_bound_args : NonCopyableNonMovable {};
-
-struct bound_args_unsafe_move {
-  template <std::derived_from<ext::like_bound_args> BoundArgs>
-  static constexpr auto from(BoundArgs&& bargs) {
-    return BoundArgs{
-        bound_args_unsafe_move{},
-        static_cast<BoundArgs&&>(bargs).unsafe_tuple_to_bind()};
-  }
-};
-
-// A binding consists of:
-//   - An "input type", most often a forwarding reference stored in the
-//     `typename` of `binding_t`.  Or, if that is a value type, then we're
-//     doing in-place construction, and the input is a "maker" object that's
-//     implicitly convertible to that value type.
-//   - "Flags" stored in `bind_info_t` enums below.  Users set them via
-//     modifier helpers like `constant{}` or `const_ref{}`.  Library authors
-//     may derive from `bind_info_t` to add custom flags, and then define a
-//     corresponding `binding_policy` specialization to handle them.
-//   - An "output type" computed **after** all the modifiers are applied
-//     via a `binding_policy`, review the docs for the standard one below.
-//
-// Each flag must default to `unset` so that the policy can dictate
-// behavior.  For example, standard policy is: non-`const` output for
-// pass-by-value, `const` for pass-by-ref.  But, with this flag + policy
-// design, an API can elect an alternate "always `const`" policy while still
-// using the standard modifier vocabulary from this file.
-enum class category_t {
-  unset = 0, // The binding policy decides. The standard policy uses "VALUE".
-  ref, // Follows reference category of the input, i.e. `InputT&&`.
-  value, // Copies or moves, depending on input ref type.
-  copy, // Like `value`, but fails on rvalue input.
-  move // Like `value`, but fails on lvalue input.
-};
-enum class constness_t {
-  // The binding policy decides.  The standard policy uses `const` for refs,
-  // `non_constant` for values.
-  unset = 0,
-  // For reference types, these both affect the underlying value type:
-  constant, // Make the input `const` if it's not already.
-  non_constant // Will NOT remove `const` from an input type
-};
-struct bind_info_t {
-  category_t category;
-  constness_t constness;
-
-  explicit bind_info_t() = default;
-  constexpr explicit bind_info_t(category_t ct, constness_t cn)
-      : category(ct), constness(cn) {}
-};
-
-// Metadata for a bound arg, capturing:
-//   - `BindingType`: the input binding type -- a reference, unless this is
-//     a `make_in_place*` binding -- see `is_binding_t_type_in_place`.
-//   - A `bind_info_t`-derived object capturing the effects of any binding
-//     modifiers (`constant`, `const_ref`, etc).
-// This is used by `binding_policy` to compute `storage_type` and
-// `signature_type`.  The values returned by `unsafe_tuple_to_bind()` should
-// be convertible to the resulting `storage_type`.
-template <std::derived_from<bind_info_t> auto, typename BindingType>
-struct binding_t {};
-
-// CAUTION: Applicable ONLY to `BT` from `binding_t<BI, BT>`.
-// Contrast to `detail::is_in_place_maker`.
-template <typename BT>
-concept is_binding_t_type_in_place = !std::is_reference_v<BT>;
-
-// Implementation detail for all the `bound_args` modifiers (`constant`,
-// `const_ref`, etc).  Concatenates multiple `Ts`, each quacking like
-// `like_bound_args`, to produce a single `like_bound_args` list.  Applies
-// `BindInfoFn` to each `bind_info_t` in the inputs' `binding_list_t`s.
-template <typename BindInfoFn, typename... Ts>
-class merge_update_bound_args : public like_bound_args {
- private:
-  using tuple_to_bind_t = decltype(detail::lite_tuple::tuple_cat(
-      std::declval<bound_args<Ts>>().unsafe_tuple_to_bind()...));
-  tuple_to_bind_t tup_;
-
- protected:
-  ~merge_update_bound_args() = default; // Only used as a base class.
-
- public:
-  // Concatenate `Ts::binding_list_t...` after mapping their `bind_info_t`s
-  // through `BindInfoFn`.
-  using binding_list_t = decltype([]<auto... BIs, typename... BTs>(
-                                      tag_t<binding_t<BIs, BTs>...>) {
-    return tag<binding_t<BindInfoFn{}(BIs), BTs>...>;
-  }(type_list_concat_t<tag_t, typename bound_args<Ts>::binding_list_t...>{}));
-
-  explicit constexpr merge_update_bound_args(bound_args<Ts>... ts) noexcept
-      : tup_(detail::lite_tuple::tuple_cat(
-            std::move(ts).unsafe_tuple_to_bind()...)) {}
-
-  // `lifetimebound` documented in `in_place_bound_args_crtp_base`
-  constexpr auto unsafe_tuple_to_bind() && noexcept [[clang::lifetimebound]] {
-    return std::move(tup_);
-  }
-
-  constexpr merge_update_bound_args(bound_args_unsafe_move, tuple_to_bind_t t)
-      : tup_(std::move(t)) {}
-};
-
-// This is cosmetic -- the point is for the signatures of `bound_args<>` and
-// similar templates to show rvalue reference bindings with `&&`.
-template <typename T>
-using deduce_bound_args_t =
-    std::conditional_t<std::derived_from<T, like_bound_args>, T, T&&>;
-
-} // namespace ext
-
-// Binds an input reference (lvalue or rvalue)
-template <typename T>
-  requires(!std::derived_from<T, ext::like_bound_args>)
-class bound_args<T> : public ext::like_bound_args {
-  static_assert(
-      std::is_reference_v<T>,
-      "Check that your deduction guide has `deduce_bound_args_t`");
-
- private:
-  T& ref_;
-
- public:
-  using binding_list_t = tag_t<ext::binding_t<ext::bind_info_t{}, T&&>>;
-
-  constexpr /*implicit*/ bound_args(T&& t) noexcept : ref_(t) {}
-
-  // `lifetimebound` documented in `in_place_bound_args_crtp_base`
-  constexpr auto unsafe_tuple_to_bind() && noexcept [[clang::lifetimebound]] {
-    return detail::lite_tuple::tuple<T&&>{static_cast<T&&>(ref_)};
-  }
-
-  constexpr bound_args(
-      ext::bound_args_unsafe_move, detail::lite_tuple::tuple<T&&> t)
-      : ref_(detail::lite_tuple::get<0>(t)) {}
-};
-
-// This specialization is instantiated when a binding modifier (usually
-// derived from `merge_update_bound_args`), or a `make_in_place*` binding,
-// gets passed to an object taking `bound_args<Ts>...`.
-//
-// It wraps another `like_bound_args`, by value.  This preserves the
-// interface (`binding_list_t`, `unsafe_tuple_to_bind`, etc) of the
-// underlying class.  It only exists to allow constructors of bound args
-// aggregates to take their inputs by-value as `bound_args<Ts>...`.
-//
-// This "always wrap" design is necessary because `like_bound_args` are both
-// immovable AND unsafe for end-users to pass by-reference.  They contain
-// references themselves, so they should only exist as prvalues for the
-// duration of one statement (so that C++ reference lifetime extension
-// guarantees safety).  So, we pass them by-value, and use the
-// `bound_args_unsafe_move` ctor to move the innards from the prvalue argument
-// into the wrapper.
-//
-// NB It is not typical for `T` to be another `bound_args`, but it's also
-// perfectly fine, compositionally speaking, so it is allowed.
-template <std::derived_from<ext::like_bound_args> T>
-class bound_args<T> : public T {
- public:
-  constexpr /*implicit*/ bound_args(T t)
-      : T(ext::bound_args_unsafe_move{}, std::move(t).unsafe_tuple_to_bind()) {}
-
-  constexpr bound_args(ext::bound_args_unsafe_move, auto tup)
-      : T(ext::bound_args_unsafe_move{}, std::move(tup)) {}
-};
-
-template <typename... Ts>
-  requires(sizeof...(Ts) != 1)
-class bound_args<Ts...>
-    : public ext::merge_update_bound_args<std::identity, Ts...> {
-  using ext::merge_update_bound_args<std::identity, Ts...>::
-      merge_update_bound_args;
-};
-template <typename... Ts>
-bound_args(Ts&&...) -> bound_args<ext::deduce_bound_args_t<Ts>...>;
-
-namespace detail { // Private details
-
-template <typename T, typename Maker>
-class in_place_bound_args_crtp_base
-    : public ::folly::bindings::ext::like_bound_args {
- private:
-  static_assert(!std::is_reference_v<T>);
-  static_assert( // This would be an unexpected usage.
-      !std::derived_from<T, ::folly::bindings::ext::like_bound_args>);
-
- protected:
-  using maker_type = Maker;
-  maker_type maker_;
-
-  constexpr in_place_bound_args_crtp_base(
-      ext::bound_args_unsafe_move, detail::lite_tuple::tuple<maker_type> t)
-      : maker_(std::move(detail::lite_tuple::get<0>(t))) {}
-
-  constexpr explicit in_place_bound_args_crtp_base(maker_type maker)
-      : maker_(std::move(maker)) {}
-
- public:
-  using binding_list_t = tag_t<ext::binding_t<ext::bind_info_t{}, T>>;
-
-  // Technically, the `lifetimebound` below is too conservative, because we
-  // hand ownership of the refs in `maker_` to the caller.  However, since
-  // `like_bound_args` must never exist for more than one statement, this
-  // should not be a problem in practical usage.
-  //
-  // The annotation's benefit is that it detects real implementation bugs.
-  // See `all_tests_run_at_build_time` for a manually compilable example.
-  // In short, if you removed this `lifetimebound`, the compiler could no
-  // longer catch this dangling ref --
-  //   // BAD: Contained prvalue `&made` becomes invalid at the `;`
-  //   auto fooMaker = make_in_place<Foo>(&made, n).unsafe_tuple_to_bind();
-  //   Foo foo = std::move(fooMaker);
-
-  // To allow in-place construction within a `tuple<..., T, ...>`, this
-  // returns a `Maker` that's implicitly convertible to `T`.
-  constexpr auto unsafe_tuple_to_bind() && noexcept [[clang::lifetimebound]] {
-    return detail::lite_tuple::tuple{std::move(maker_)};
-  }
-};
-
-// Both "maker" classes are move-only to help keep them single-use.
-template <typename T, typename... Args>
-class in_place_args_maker : private MoveOnly {
- private:
-  // `&&` allows binding rvalues. Safe, since a binding lives for 1 statement.
-  detail::lite_tuple::tuple<Args&&...> arg_tup_;
-
- protected:
-  template <typename, typename...>
-  friend class in_place_bound_args;
-
-  constexpr /*implicit*/ in_place_args_maker(
-      Args&&... as [[clang::lifetimebound]]) noexcept
-      : arg_tup_{static_cast<Args&&>(as)...} {}
-
- public:
-  // This implicit conversion allows direct construction inside of `tuple` e.g.
-  constexpr /*implicit*/ operator T() && {
-    return lite_tuple::apply(
-        [](auto&&... as) { return T{static_cast<Args&&>(as)...}; },
-        std::move(arg_tup_));
-  }
-  // Power users may want to rewrite the args of an in-place binding.
-  constexpr auto&& release_arg_tuple() && noexcept [[clang::lifetimebound]] {
-    return std::move(arg_tup_);
-  }
-};
-
-// NB: `Args` are deduced by `make_in_place` as forwarding references
-template <typename T, typename... Args>
-class in_place_bound_args
-    : public in_place_bound_args_crtp_base<T, in_place_args_maker<T, Args...>> {
- protected:
-  template <typename, typename... As>
-  friend constexpr auto ::folly::bindings::make_in_place(As&&...);
-
-  using base =
-      in_place_bound_args_crtp_base<T, in_place_args_maker<T, Args...>>;
-  constexpr explicit in_place_bound_args(
-      Args&&... args [[clang::lifetimebound]])
-      : base{{static_cast<Args&&>(args)...}} {}
-
- public:
-  constexpr in_place_bound_args(ext::bound_args_unsafe_move u, auto t)
-      : base{u, std::move(t)} {}
-};
-
-template <typename T, typename Fn, typename... Args>
-class in_place_fn_maker : private MoveOnly {
- private:
-  Fn fn_; // May contain refs; ~safe since a binding lives for 1 statement.
-  detail::lite_tuple::tuple<Args&&...> arg_tup_;
-
- protected:
-  template <typename, typename, typename...>
-  friend class in_place_fn_bound_args;
-
-  constexpr /*implicit*/ in_place_fn_maker(
-      Fn fn, Args&&... as [[clang::lifetimebound]])
-      : fn_(std::move(fn)), arg_tup_{static_cast<Args&&>(as)...} {}
-
- public:
-  // This implicit conversion allows direct construction inside of `tuple` e.g.
-  constexpr /*implicit*/ operator T() && {
-    return lite_tuple::apply(fn_, arg_tup_);
-  }
-};
-
-// NB: `Args` are deduced by `make_in_place` as forwarding references
-template <typename T, typename Fn, typename... Args>
-class in_place_fn_bound_args
-    : public in_place_bound_args_crtp_base<
-          T,
-          in_place_fn_maker<T, Fn, Args...>> {
- protected:
-  template <typename... As>
-  friend constexpr auto ::folly::bindings::make_in_place_with(auto, As&&...);
-
-  using base =
-      in_place_bound_args_crtp_base<T, in_place_fn_maker<T, Fn, Args...>>;
-  constexpr explicit in_place_fn_bound_args(
-      Fn fn, Args&&... args [[clang::lifetimebound]])
-      : base{{std::move(fn), static_cast<Args&&>(args)...}} {}
-
- public:
-  constexpr in_place_fn_bound_args(ext::bound_args_unsafe_move u, auto t)
-      : base{u, std::move(t)} {}
-};
-
-// If using this with `BT` from `binding_t<BI, BT>`, do not `remove_reference`
-// first, since such `BT` is always a value for in-place bindings.
-template <typename T>
-concept is_in_place_maker = instantiated_from<T, in_place_fn_maker> ||
-    instantiated_from<T, in_place_args_maker>;
-
-using constant_bind_info = decltype([](auto bi) {
-  bi.constness = ext::constness_t::constant;
-  return bi;
-});
-
-using non_constant_bind_info = decltype([](auto bi) {
-  bi.constness = ext::constness_t::non_constant;
-  return bi;
-});
-
-using const_ref_bind_info = decltype([](auto bi) {
-  bi.category = ext::category_t::ref;
-  bi.constness = ext::constness_t::constant;
-  return bi;
-});
-
-using mut_ref_bind_info = decltype([](auto bi) {
-  bi.category = ext::category_t::ref;
-  bi.constness = ext::constness_t::non_constant;
-  return bi;
-});
-
-} // namespace detail
-
-// `make_in_place` and `make_in_place_with` construct non-movable,
-// non-copyable types in their final location.
-//
-// CAREFUL: As with other `bound_args{}`, the returned object stores references
-// to `args` to avoid unnecessary move-copies.  A power user may wish to write
-// a function that returns a binding, which OWNS some values.  For example, you
-// may need to avoid a stack-use-after-free such as this one:
-//
-//   auto makeFoo(int n) { return make_in_place<Foo>(n); } // BAD: `n` is dead
-//
-// To avoid the bug, either take `n` by-reference (often preferred), or store
-// your values inside a `make_in_place_with` callable:
-//
-//   auto makeFoo(int n) {
-//     return make_in_place_with([n]() { return Foo{n}; });
-//   }
-template <typename T, typename... Args>
-constexpr auto make_in_place(Args&&... args [[clang::lifetimebound]]) {
-  return detail::in_place_bound_args<T, Args...>{static_cast<Args&&>(args)...};
-}
-// This is second-choice compared to `make_in_place` because:
-//   - Dangling references may be hidden inside `make_fn` captures --
-//     `clang` offers no `lifetimebound` analysis for these (yet?).
-//   - The type signature of the `in_place_bound_args` includes a lambda.
-// CAREFUL: While `make_fn` is taken by-value, `args` are stored as references,
-// as in `make_in_place`.
-template <typename... Args>
-constexpr auto make_in_place_with(
-    auto make_fn, Args&&... args [[clang::lifetimebound]]) {
-  return detail::in_place_fn_bound_args<
-      std::invoke_result_t<decltype(make_fn), Args&&...>,
-      decltype(make_fn),
-      Args...>{std::move(make_fn), static_cast<Args&&>(args)...};
-}
-
-// The below "binding modifiers" all return an immovable bound args list,
-// meant to be passed only by-value, as a prvalue.  They can all take unary,
-// or plural `like_bound_args` as inputs.  Their only difference from
-// `bound_args{values...}` is that these modifiers override some aspect of
-// `bind_info_t`s on on all the bindings they contain.
-//
-// Unlike standard C++, the "constant" and "ref" modifiers commute, avoiding
-// that common footgun.
-//
-// You can think of these analogously to value category specifiers for
-// member variables of a struct -- e.g.
-//
-//   (1) `mut_ref{}` asks to store a non-const reference, of
-//        the same reference category as the input.  Or, see `const_ref{}`.
-//   (2) `constant{}` stores the input as a `const` value (under the default
-//       `binding_policy`).  Unlike `std::as_const` this allows you to move
-//        a non-const reference into a `const` storage location.
-//
-// Specifiers can be overridden, e.g. you could (but should not!) express
-// `mut_ref{}` as `non_constant{const_ref{}}`.
-//
-// There's currently no user-facing `by_ref{}`, which would leave the
-// `constness` of the binding to be defaulted by the `binding_policy` below.
-// If a use-case arises, the test already contains its trivial implementation.
-
-template <typename... Ts>
-struct constant
-    : ext::merge_update_bound_args<detail::constant_bind_info, Ts...> {
-  using ext::merge_update_bound_args<detail::constant_bind_info, Ts...>::
-      merge_update_bound_args;
-};
-template <typename... Ts>
-constant(Ts&&...) -> constant<ext::deduce_bound_args_t<Ts>...>;
-
-template <typename... Ts>
-struct non_constant
-    : ext::merge_update_bound_args<detail::non_constant_bind_info, Ts...> {
-  using ext::merge_update_bound_args<detail::non_constant_bind_info, Ts...>::
-      merge_update_bound_args;
-};
-template <typename... Ts>
-non_constant(Ts&&...) -> non_constant<ext::deduce_bound_args_t<Ts>...>;
-
-template <typename... Ts>
-struct const_ref
-    : ext::merge_update_bound_args<detail::const_ref_bind_info, Ts...> {
-  using ext::merge_update_bound_args<detail::const_ref_bind_info, Ts...>::
-      merge_update_bound_args;
-};
-template <typename... Ts>
-const_ref(Ts&&...) -> const_ref<ext::deduce_bound_args_t<Ts>...>;
-
-template <typename... Ts>
-struct mut_ref
-    : ext::merge_update_bound_args<detail::mut_ref_bind_info, Ts...> {
-  using ext::merge_update_bound_args<detail::mut_ref_bind_info, Ts...>::
-      merge_update_bound_args;
-};
-template <typename... Ts>
-mut_ref(Ts&&...) -> mut_ref<ext::deduce_bound_args_t<Ts>...>;
-
-// Future: Add `copied()` and `moved()` modifiers so the user can ensure
-// pass-by-value with copy-, or move-copy semantics.  This enforcement
-// already exists in `binding_policy` and `category_t`.
-
-namespace ext { // For extension authors -- public details
-
-template <typename, typename = void>
-class binding_policy;
-
-// This is a separate class so that libraries can customize the binding
-// policy enacted by `binding_policy` by detecting their custom fields in
-// `BI`.  See `named/Binding.h` for an example.
-//
-// IMPORTANT:
-//  - Only specialize `binding_policy` for the specific derived classes
-//    of `bind_info_t` that you own -- DO NOT overmatch!
-//  - DO delegate handling of the base `bind_info_t` to this specialization, by
-//    slicing your input BI. Example:
-//      using basics = binding_policy<binding_t<bind_info_t{BI}, BindingType>>;
-//    Any deviations from the standard policy are likely to confuse users &
-//    readers of your library, and are probably not worth it.  If you REALLY
-//    need to deviate (ex: default bindings to `const`), make the name of
-//    your API reflect this (ex: `fooDefaultConst()`).
-template <auto BI, typename BindingType>
-// Formulated as a constraint to prevent object slicing
-  requires std::same_as<decltype(BI), bind_info_t>
-class binding_policy<binding_t<BI, BindingType>> {
-  static_assert(
-      !is_binding_t_type_in_place<BindingType> ||
-          BI.category != category_t::ref,
-      "`const_ref` / `mut_ref` is incompatible with `make_in_place*`");
-
- protected:
-  // Future: This **might** compile faster with a family of explicit
-  // specializations, see e.g. `folly::like_t`'s implementation.
-  template <typename T>
-  using add_const_inside_ref = std::conditional_t<
-      std::is_rvalue_reference_v<T>,
-      typename std::add_const<std::remove_reference_t<T>>::type&&,
-      std::conditional_t<
-          std::is_lvalue_reference_v<T>,
-          typename std::add_const<std::remove_reference_t<T>>::type&,
-          typename std::add_const<std::remove_reference_t<T>>::type>>;
-
-  constexpr static auto project_type() {
-    if constexpr (BI.category == category_t::ref) {
-      // By-reference: `const` by default
-      if constexpr (BI.constness == constness_t::non_constant) {
-        return std::type_identity<BindingType&&>{}; // Leave existing `const`
-      } else {
-        return std::type_identity<add_const_inside_ref<BindingType>&&>{};
-      }
-    } else {
-      if constexpr (BI.category == category_t::copy) {
-        static_assert(!std::is_rvalue_reference_v<BindingType>);
-      } else if constexpr (BI.category == category_t::move) {
-        static_assert(std::is_rvalue_reference_v<BindingType>);
-      } else {
-        static_assert(
-            (BI.category == category_t::value) ||
-            (BI.category == category_t::unset));
-      }
-      // By-value: non-`const` by default
-      using UnrefT = std::remove_reference_t<BindingType>;
-      if constexpr (BI.constness == constness_t::constant) {
-        return std::type_identity<const UnrefT>{};
-      } else {
-        return std::type_identity<UnrefT>{};
-      }
-    }
-  }
-
- public:
-  using storage_type = typename decltype(project_type())::type;
-  // Why is this here?  With named bindings, we want the signature of a
-  // binding list to show the identifier's name to the user.
-  using signature_type = storage_type;
-};
-
-} // namespace ext
-} // namespace folly::bindings
-
-FOLLY_POP_WARNING
diff --git a/folly/folly/lang/Bits.h b/folly/folly/lang/Bits.h
--- a/folly/folly/lang/Bits.h
+++ b/folly/folly/lang/Bits.h
@@ -145,7 +145,7 @@
 /// Return the 1-based index of the most significant bit which is set.
 /// For x > 0, findLastSet(x) == 1 + floor(log2(x)).
 template <typename T>
-inline constexpr unsigned int findLastSet(T const v) {
+FOLLY_ALWAYS_INLINE constexpr unsigned int findLastSet(T const v) {
   using U0 = unsigned int;
   using U1 = unsigned long int;
   using U2 = unsigned long long int;
@@ -201,7 +201,7 @@
 }
 
 template <class T>
-inline constexpr T nextPowTwo(T const v) {
+FOLLY_ALWAYS_INLINE constexpr T nextPowTwo(T const v) {
   static_assert(std::is_unsigned<T>::value, "signed type");
   return v ? (T(1) << findLastSet(v - 1)) : T(1);
 }
@@ -241,7 +241,7 @@
 struct n_least_significant_bits_fn {
   static_assert(detail::supported_in_bits_operations_v<T>);
 
-  FOLLY_NODISCARD constexpr T operator()(std::uint32_t n) const {
+  [[nodiscard]] constexpr T operator()(std::uint32_t n) const {
     if (!folly::is_constant_evaluated_or(true)) {
       compiler_may_unsafely_assume(n <= sizeof(T) * 8);
 
@@ -272,7 +272,7 @@
 struct n_most_significant_bits_fn {
   static_assert(detail::supported_in_bits_operations_v<T>);
 
-  FOLLY_NODISCARD constexpr T operator()(std::uint32_t n) const {
+  [[nodiscard]] constexpr T operator()(std::uint32_t n) const {
     if (!folly::is_constant_evaluated_or(true)) {
       compiler_may_unsafely_assume(n <= sizeof(T) * 8);
 
@@ -303,7 +303,7 @@
 /// Clears n least significant (right) bits. Other bits stay the same.
 struct clear_n_least_significant_bits_fn {
   template <typename T>
-  FOLLY_NODISCARD constexpr T operator()(T x, std::uint32_t n) const {
+  [[nodiscard]] constexpr T operator()(T x, std::uint32_t n) const {
     static_assert(detail::supported_in_bits_operations_v<T>);
 
     // alternative is to do two shifts but that has
@@ -321,7 +321,7 @@
 /// Sets n least significant (right) bits. Other bits stay the same.
 struct set_n_least_significant_bits_fn {
   template <typename T>
-  FOLLY_NODISCARD constexpr T operator()(T x, std::uint32_t n) const {
+  [[nodiscard]] constexpr T operator()(T x, std::uint32_t n) const {
     static_assert(detail::supported_in_bits_operations_v<T>);
 
     // alternative is to do two shifts but that has
@@ -338,7 +338,7 @@
 /// Clears n most significant (left) bits. Other bits stay the same.
 struct clear_n_most_significant_bits_fn {
   template <typename T>
-  FOLLY_NODISCARD constexpr T operator()(T x, std::uint32_t n) const {
+  [[nodiscard]] constexpr T operator()(T x, std::uint32_t n) const {
     static_assert(detail::supported_in_bits_operations_v<T>);
 
     if (!folly::is_constant_evaluated_or(true)) {
@@ -366,7 +366,7 @@
 /// Sets n most significant (left) bits. Other bits stay the same.
 struct set_n_most_significant_bits_fn {
   template <typename T>
-  FOLLY_NODISCARD constexpr T operator()(T x, std::uint32_t n) const {
+  [[nodiscard]] constexpr T operator()(T x, std::uint32_t n) const {
     static_assert(detail::supported_in_bits_operations_v<T>);
     return x | n_most_significant_bits<T>(n);
   }
@@ -485,7 +485,7 @@
 ///
 struct get_bit_at_fn {
   template <typename Uint>
-  FOLLY_NODISCARD constexpr bool operator()(
+  [[nodiscard]] constexpr bool operator()(
       const Uint* ptr, std::size_t idx) const noexcept {
     static_assert(std::is_unsigned_v<std::remove_cv_t<Uint>>);
     static_assert(!std::is_same_v<std::remove_cv_t<Uint>, bool>);
diff --git a/folly/folly/lang/BitsClass.h b/folly/folly/lang/BitsClass.h
--- a/folly/folly/lang/BitsClass.h
+++ b/folly/folly/lang/BitsClass.h
@@ -57,7 +57,7 @@
 struct BitsTraits<
     Unaligned<T>,
     typename std::enable_if<(std::is_integral<T>::value)>::type> {
-  typedef T UnderlyingType;
+  using UnderlyingType = T;
   static T load(const Unaligned<T>& x) { return x; }
   static void store(Unaligned<T>& x, T v) { x = v; }
   static T loadRMW(const Unaligned<T>& x) {
@@ -74,7 +74,7 @@
 struct BitsTraits<
     UnalignedNoASan<T>,
     typename std::enable_if<(std::is_integral<T>::value)>::type> {
-  typedef T UnderlyingType;
+  using UnderlyingType = T;
   static T FOLLY_DISABLE_ADDRESS_SANITIZER load(const UnalignedNoASan<T>& x) {
     return x.value;
   }
@@ -97,7 +97,7 @@
 struct BitsTraits<
     T,
     typename std::enable_if<(std::is_integral<T>::value)>::type> {
-  typedef T UnderlyingType;
+  using UnderlyingType = T;
   static T load(const T& x) { return x; }
   static void store(T& x, T v) { x = v; }
   static T loadRMW(const T& x) {
@@ -119,8 +119,8 @@
  */
 template <class T, class Traits = detail::BitsTraits<T>>
 struct Bits {
-  typedef typename Traits::UnderlyingType UnderlyingType;
-  typedef T type;
+  using UnderlyingType = typename Traits::UnderlyingType;
+  using type = T;
   static_assert(sizeof(T) == sizeof(UnderlyingType), "Size mismatch");
 
   /**
@@ -294,7 +294,10 @@
 inline auto Bits<T, Traits>::innerGet(const T* p, size_t offset, size_t count)
     -> UnderlyingType {
 #ifdef __BMI__
-  return _bextr_u64(Traits::load(*p), offset, count);
+  return _bextr_u64(
+      Traits::load(*p),
+      static_cast<unsigned int>(offset),
+      static_cast<unsigned int>(count));
 #else
   return (Traits::load(*p) >> offset) & ones(count);
 #endif
diff --git a/folly/folly/lang/Exception.cpp b/folly/folly/lang/Exception.cpp
--- a/folly/folly/lang/Exception.cpp
+++ b/folly/folly/lang/Exception.cpp
@@ -373,6 +373,15 @@
   return !target || target->__do_catch(type, &object, 1) ? object : nullptr;
 }
 
+std::size_t exception_ptr_use_count_(std::exception_ptr const& ptr) noexcept {
+  if (!ptr) {
+    return 0;
+  }
+  auto object = reinterpret_cast<void* const&>(ptr);
+  auto exception = static_cast<abi::__cxa_refcounted_exception*>(object) - 1;
+  return __atomic_load_n(&exception->referenceCount, __ATOMIC_RELAXED);
+}
+
 #endif // defined(__GLIBCXX__)
 
 #if defined(_LIBCPP_VERSION) && !defined(__FreeBSD__)
@@ -477,6 +486,16 @@
   return !target || starget->can_catch(type, object) ? object : nullptr;
 }
 
+std::size_t exception_ptr_use_count_(std::exception_ptr const& ptr) noexcept {
+  if (!ptr) {
+    return 0;
+  }
+  auto object = cxxabi_get_object(ptr);
+  return cxxabi_with_cxa_exception(object, [](auto exception) -> long {
+    return __atomic_load_n(&exception->referenceCount, __ATOMIC_RELAXED);
+  });
+}
+
 #endif // defined(_LIBCPP_VERSION) && !defined(__FreeBSD__)
 
 #if defined(__FreeBSD__)
@@ -515,6 +534,15 @@
   return !target || starget->__do_catch(type, &object, 1) ? object : nullptr;
 }
 
+std::size_t exception_ptr_use_count_(std::exception_ptr const& ptr) noexcept {
+  if (!ptr) {
+    return 0;
+  }
+  auto object = reinterpret_cast<void* const&>(ptr);
+  auto exception = static_cast<abi::__cxa_exception*>(object) - 1;
+  return __atomic_load_n(&exception->referenceCount, __ATOMIC_RELAXED);
+}
+
 #endif // defined(__FreeBSD__)
 
 #if defined(_WIN32)
@@ -525,9 +553,14 @@
       DecodePointer(const_cast<void*>(static_cast<void const*>(ptr))));
 }
 
+static std::shared_ptr<EHExceptionRecord> const& win32_get_record_sptr(
+    std::exception_ptr const& ptr) noexcept {
+  return reinterpret_cast<std::shared_ptr<EHExceptionRecord> const&>(ptr);
+}
+
 static EHExceptionRecord* win32_get_record(
     std::exception_ptr const& ptr) noexcept {
-  return reinterpret_cast<std::shared_ptr<EHExceptionRecord> const&>(ptr).get();
+  return win32_get_record_sptr(ptr).get();
 }
 
 static bool win32_eptr_throw_info_ptr_is_encoded() {
@@ -631,6 +664,10 @@
   return nullptr;
 }
 
+std::size_t exception_ptr_use_count_(std::exception_ptr const& ptr) noexcept {
+  return win32_get_record_sptr(ptr).use_count();
+}
+
 #endif // defined(_WIN32)
 
 } // namespace detail
@@ -692,6 +729,14 @@
 #endif
 }
 
+std::size_t exception_ptr_use_count(std::exception_ptr const& ptr) noexcept {
+  return detail::exception_ptr_use_count_(ptr);
+}
+
+bool exception_ptr_unique(std::exception_ptr const& ptr) noexcept {
+  return exception_ptr_use_count(ptr) == 1;
+}
+
 namespace detail {
 
 template <typename Try>
@@ -846,10 +891,36 @@
 exception_shared_string::exception_shared_string(
     char const* const str, std::size_t const len)
     : tagged_what_{vtag<false>, from_state(state::make(str, len))} {}
+
 exception_shared_string::exception_shared_string(
     exception_shared_string const& that) noexcept
     : tagged_what_{
           (state::copy(to_state(that.tagged_what_)), that.tagged_what_)} {}
+
+exception_shared_string& exception_shared_string::operator=(
+    exception_shared_string const& that) noexcept {
+  if (this != &that) {
+    ruin_state();
+    state::copy(to_state(that.tagged_what_));
+    tagged_what_ = that.tagged_what_;
+  }
+  return *this;
+}
+
+#if FOLLY_CPLUSPLUS >= 202002 && !defined(__NVCC__)
+
+exception_shared_string& exception_shared_string::operator=(
+    exception_shared_string&& that) noexcept {
+  if (this != &that) {
+    ruin_state();
+    tagged_what_ = that.tagged_what_;
+    that.tagged_what_ =
+        tagged_what_t{vtag<true>, ""}; // safe-to-read moved-out state
+  }
+  return *this;
+}
+
+#endif
 
 void exception_shared_string::ruin_state() noexcept {
   state::ruin(to_state(tagged_what_));
diff --git a/folly/folly/lang/Exception.h b/folly/folly/lang/Exception.h
--- a/folly/folly/lang/Exception.h
+++ b/folly/folly/lang/Exception.h
@@ -528,6 +528,26 @@
   return static_cast<T*>(object);
 }
 
+//  exception_ptr_use_count
+//
+//  Returns the reference count of the stored exception.
+//
+//  Returns 0 for an empty exception_ptr. Otherwise, returns the number of
+//  exception_ptr instances that refer to the same stored exception object.
+//
+//  Analogous to std::shared_ptr::use_count.
+std::size_t exception_ptr_use_count(std::exception_ptr const& ptr) noexcept;
+
+//  exception_ptr_unique
+//
+//  Returns whether the stored exception is uniquely referenced.
+//
+//  Returns false for an empty exception_ptr. Otherwise, returns true if this
+//  is the only exception_ptr instance referring to the stored exception object.
+//
+//  Analogous to std::shared_ptr::unique.
+bool exception_ptr_unique(std::exception_ptr const& ptr) noexcept;
+
 /// exception_ptr_try_get_object_exact_fast
 ///
 /// Returns the address of the stored exception as if it were upcast to the
@@ -539,7 +559,7 @@
 /// or false.
 template <typename T, typename... S>
 T* exception_ptr_try_get_object_exact_fast(
-    std::exception_ptr const& ptr, tag_t<S...>) noexcept {
+    std::exception_ptr const& ptr, tag_t<S...> /*unused*/) noexcept {
   static_assert((std::is_convertible_v<S*, T*> && ...));
   if (!kHasRtti || !ptr || !exception_ptr_access()) {
     return nullptr;
@@ -605,6 +625,13 @@
 /// `get_exception<Ex>(v)` is meant to become the default way for accessing
 /// exception-containers in `folly`.
 ///
+///   if (auto ex = get_exception<MyError>(eptr)) {
+///     LOG(INFO) << ex->myErrorDetails();
+///   }
+///
+/// NB: Above, `ex` quacks like `MyError*`, but in some cases may be a proxy
+/// class type with `->`.  Avoid having to know by writing `auto`, not `auto*`.
+///
 /// For the less-common scenario where you need mutable access to an error, use
 /// `get_mutable_exception<Ex>(v)`.  This is a separate verb because:
 ///    - Mutable exception access is rare.  It may run into thread-safety bugs
@@ -613,28 +640,37 @@
 ///    - Making mutable access explicit enables no-alloctions, no-atomics
 ///      optimizations for the `const`-access path.
 ///
-/// Both verbs return:
-///   - `nullptr` if `v` is of a variant type, but is not in an "error" state,
-///   - A pointer to the `Ex` held by `v`, if it holds an error whose type
-///     `From` permits `std::is_convertible<From*, Ex*>`,
-///   - `nullptr` for errors incompatible with `Ex*`.
+/// ## Implementing this protocol
 ///
-/// In addition to the `std::exception_ptr` support above, a type can support
-/// this verb by providing member functions taking `get_exception_tag_t`.  For
-/// an example, see `ExceptionWrapper.h`.  Requirements:
+/// Any exception container may provide these verbs via member functions
+/// `get_exception` and `get_mutable_exception` taking `get_exception_tag_t`.
+/// For an example, see `ExceptionWrapper.h`.  Requirements:
+///   - Return a bare pointer to `Ex`, or a pointer-like with:
+///     - `operator bool` to test if an exception was found,
+///     - `operator*` and `operator->` to access the exception object,
+///       respecting the const-qualification of `Ex`.
+///   - Returns null if the container is not in an "error" state.
+///   - Returns null if the container has an error incompatible with `Ex`.
+///   - Returns a `Ex` pointer-like into the container, if it holds an error
+///     whose type `From` permits `std::is_convertible<From*, Ex*>`,
+///   - `nullptr` for errors incompatible with `Ex*`.
+///   - `const` access via `get_exception`, mutable via `get_mutable_exception`.
 ///   - `noexcept`
-///   - returns `Ex*` or `const Ex*` depending on the verb.
 ///
-/// This is most efficient when `Ex` matches the exact stored type, or when the
+/// This header provides `get_exception` support for `std::exception_ptr`.
+///
+/// ## Design notes
+///
+/// Lookup is more efficient if `Ex` matches the exact stored type, or if the
 /// type alias `Ex::folly_get_exception_hint_types` provides a correct hint.
 ///
 /// NB: `result<T>` supports `get_exception<Ex>(res)`, but `Try<T>` currently
 /// omits `get_exception(get_exception_tag_t)`, because that might encourage
 /// "empty state" bugs:
 ///
-///   if (auto* ex = get_exception<MyError>(tryData)) {
+///   if (auto ex = get_exception<MyError>(tryData)) {
 ///     // handle error
-///   } else {
+///   } else { // Should've checked `hasValue()`!
 ///     doStuff(tryData.value()); // Oops, may throw `UsingUninitializedTry`!
 ///   }
 ///
@@ -644,7 +680,7 @@
 class get_exception_fn {
  public:
   template <typename Src>
-  const Ex* operator()(
+  constexpr auto operator()(
       [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] const Src& src) const noexcept {
     if constexpr (std::is_same_v<Src, std::exception_ptr>) {
       return exception_ptr_get_object_hint<const Ex>(src);
@@ -652,15 +688,15 @@
       constexpr get_exception_tag_t passkey;
       static_assert( // Return type & `noexcept`ness must match
           std::is_same_v<
-              const Ex*,
-              decltype(src.template get_exception<Ex>(passkey))> &&
-          noexcept(noexcept(src.template get_exception<Ex>(passkey))));
+              const Ex&,
+              decltype(*src.template get_exception<Ex>(passkey))> &&
+          noexcept(noexcept(*src.template get_exception<Ex>(passkey))));
       return src.template get_exception<Ex>(passkey);
     }
   }
   // For a mutable ptr, use `folly::get_mutable_exception<Ex>(v)` instead.
   template <typename Src>
-  const Ex* operator()(
+  constexpr auto operator()(
       [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] Src& s) const noexcept {
     return operator()(std::as_const(s));
   }
@@ -676,16 +712,16 @@
 class get_mutable_exception_fn {
  public:
   template <typename Src>
-  Ex* operator()([[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] Src& src) const noexcept {
+  auto operator()([[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] Src& src) const noexcept {
     if constexpr (std::is_same_v<Src, std::exception_ptr>) {
       return exception_ptr_get_object_hint<Ex>(src);
     } else {
       constexpr get_exception_tag_t passkey;
       static_assert( // Return type & `noexcept`ness must match
           std::is_same_v<
-              Ex*,
-              decltype(src.template get_mutable_exception<Ex>(passkey))> &&
-          noexcept(noexcept(src.template get_mutable_exception<Ex>(passkey))));
+              Ex&,
+              decltype(*src.template get_mutable_exception<Ex>(passkey))> &&
+          noexcept(noexcept(*src.template get_mutable_exception<Ex>(passkey))));
       return src.template get_mutable_exception<Ex>(passkey);
     }
   }
@@ -705,8 +741,33 @@
 template <typename Ex = std::exception>
 inline constexpr get_mutable_exception_fn<Ex> get_mutable_exception{};
 
+class rich_error_base;
+class rich_error_code_query;
+
 namespace detail {
 
+// The template declaration in `rich_error_code.h` explains this setup, where
+// neither of the headers includes the other.
+//
+// CRITICALLY IMPORTANT: Do not add or change specializations of this template
+// without reading the corresponding docblock in `rich_error_code.h`, which
+// explains the ODR risk inherent in this setup, and why it is currently safe.
+template <typename T, typename /*SFINAE*/>
+struct get_rich_error_code_traits;
+template <typename T>
+struct get_rich_error_code_traits<
+    T,
+    std::enable_if_t<
+        !std::is_same_v<T, std::exception_ptr> &&
+        !std::is_base_of_v<rich_error_base, T>>> {
+  static constexpr void retrieve_code(
+      const T& container, rich_error_code_query& query) {
+    if (auto ex = folly::get_exception<folly::rich_error_base>(container)) {
+      ex->retrieve_code(query);
+    }
+  }
+};
+
 // The libc++ and cpplib implementations do not have a move constructor or a
 // move-assignment operator. To avoid refcount operations, we must improvise.
 // The libstdc++ implementation has a move constructor and a move-assignment
@@ -742,7 +803,8 @@
   }
 
   template <typename F, typename E = decltype(FOLLY_DECLVAL(F&)())>
-  FOLLY_ERASE explicit constexpr make_exception_ptr_with_arg_(tag_t<F>) noexcept
+  FOLLY_ERASE explicit constexpr make_exception_ptr_with_arg_(
+      tag_t<F> /*unused*/) noexcept
       : size{sizeof(E)},
         type{FOLLY_TYPE_INFO_OF(E)},
         ctor{make<F, E>},
@@ -834,12 +896,12 @@
   }
   template <typename E, typename... A>
   FOLLY_ERASE std::exception_ptr operator()(
-      std::in_place_type_t<E>, A&&... a) const noexcept {
+      std::in_place_type_t<E> /*unused*/, A&&... a) const noexcept {
     return operator()(make<E, make_arg_t<A&&>...>(static_cast<A&&>(a)...));
   }
   template <typename E>
   FOLLY_ERASE std::exception_ptr operator()(
-      std::in_place_t, E&& e) const noexcept {
+      std::in_place_t /*unused*/, E&& e) const noexcept {
     constexpr auto tag = std::in_place_type<remove_cvref_t<E>>;
     check_(FOLLY_TYPE_INFO_OF(std::decay_t<E>), FOLLY_TYPE_INFO_OF(e));
     return operator()(tag, static_cast<E&&>(e));
@@ -911,6 +973,7 @@
       }
     }
 #endif
+    // NEVER make this `constexpr`; `what()` explains why.
     tagged_what_t(vtag_t<false> /*allocated*/, const char* p)
         : p_{reinterpret_cast<const char*>(
               non_literal_mask | reinterpret_cast<uintptr_t>(p))} {
@@ -920,14 +983,26 @@
     bool is_literal() const noexcept {
       return !(non_literal_mask & reinterpret_cast<uintptr_t>(p_));
     }
+#if FOLLY_CPLUSPLUS >= 202002
+    constexpr const char* what() const noexcept {
+      if (std::is_constant_evaluated()) {
+        // Only the literal ctor is `constexpr`, so we can assume no
+        // bit-twiddling is needed.  Consteval code cannot do Pointer bithacks.
+        return p_;
+      }
+      return reinterpret_cast<const char*>(
+          ~non_literal_mask & reinterpret_cast<uintptr_t>(p_));
+    }
+#else
     const char* what() const noexcept {
       return reinterpret_cast<const char*>(
           ~non_literal_mask & reinterpret_cast<uintptr_t>(p_));
     }
+#endif
   };
   static_assert(sizeof(tagged_what_t) == sizeof(void*));
 
-  const tagged_what_t tagged_what_;
+  tagged_what_t tagged_what_;
 
   exception_shared_string(std::size_t, format_sig_&, void*);
 
@@ -957,7 +1032,23 @@
             size, ffun_<F>, &reinterpret_cast<unsigned char&>(func)) {}
 
   exception_shared_string(exception_shared_string const&) noexcept;
+  exception_shared_string& operator=(exception_shared_string const&) noexcept;
 
+  // It would be extra effort to implement move support in C++17, but there is
+  // currently no demand for it.
+#if FOLLY_CPLUSPLUS >= 202002 && !defined(__NVCC__)
+  constexpr exception_shared_string(exception_shared_string&& that) noexcept
+      : tagged_what_{that.tagged_what_} {
+    that.tagged_what_ =
+        tagged_what_t{vtag<true>, ""}; // safe-to-read moved-out state
+  }
+
+  exception_shared_string& operator=(exception_shared_string&&) noexcept;
+#else
+  exception_shared_string(exception_shared_string&&) = delete;
+  exception_shared_string& operator=(exception_shared_string&&) = delete;
+#endif
+
 #if FOLLY_CPLUSPLUS >= 202002 && defined(__cpp_lib_is_constant_evaluated)
   constexpr ~exception_shared_string() {
     if (!std::is_constant_evaluated()) {
@@ -968,9 +1059,12 @@
   ~exception_shared_string() { ruin_state(); }
 #endif
 
-  void operator=(exception_shared_string const&) = delete;
-
-  char const* what() const noexcept { return tagged_what_.what(); }
+#if FOLLY_CPLUSPLUS >= 202002
+  constexpr
+#endif
+      char const* what() const noexcept {
+    return tagged_what_.what();
+  }
 };
 
 } // namespace folly
diff --git a/folly/folly/lang/Hint-inl.h b/folly/folly/lang/Hint-inl.h
--- a/folly/folly/lang/Hint-inl.h
+++ b/folly/folly/lang/Hint-inl.h
@@ -87,6 +87,7 @@
 template <typename T, typename D = std::decay_t<T>>
 using compiler_must_force_indirect = std::bool_constant<
     !std::is_trivially_copyable_v<D> || //
+    !std::is_copy_constructible_v<D> || //
     sizeof(long) < sizeof(D) || //
     std::is_pointer<D>::value>;
 
diff --git a/folly/folly/lang/MustUseImmediately.h b/folly/folly/lang/MustUseImmediately.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/MustUseImmediately.h
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <type_traits>
+
+#include <folly/Utility.h>
+#include <folly/lang/SafeAlias-fwd.h>
+
+#if FOLLY_CPLUSPLUS >= 202002L
+#include <folly/detail/tuple.h>
+#endif
+
+FOLLY_PUSH_WARNING
+FOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS
+
+// The `ext` namespace is not for end-users, but for library authors.
+// Essentially, this is a public analog of `folly::detail`.  If you use it,
+// expect to see churn on the API, and to be asked to help with changes.
+namespace folly::ext {
+
+// ## What are must-use-immediately types, and how should I handle them?
+//
+// Types satisfying the must-use-immediately concept follow these principles:
+//  - The type is always immovable.
+//  - APIs always take the type by-value, never by-reference.
+//  - It typically supports the `unsafe_mover()` protocol, documented in detail
+//    in the doc of the user-facing `must_use_immediately_unsafe_mover()`.
+//
+// Types usually need to become must-use-immediately because they contain
+// references whose lifetime isn't safe enough for the context. Examples:
+//
+//  - In `folly/lang/bind/`, types often contain references to prvalues. Its
+//    ad hoc reimplementation of must-use-immediately guarantees that refs
+//    inside bindings remain valid. Future: Convert to `MustUseImmediately.h`.
+//
+//  - In `folly/coro`:
+//      * Must-use-immediately coroutines (like `now_task`) let people
+//        **safely** take args by-reference, which is otherwise unsafe.
+//      * Types that contain refs with lifetime unknown, or too-short for safe
+//        async usage will measure `safe_alias_of < closure_min_arg_safety`.
+//      * Safe APIs (like pipelines and `then`) make types must-use-immediately
+//        based on their `safe_alias` measurement, or the must-use-immediately
+//        status of their parts.
+//
+// ## General protocol for creating must-use-immediately types
+//
+// To create a must-use-immediately type, either:
+//
+//   - If your type is movable and is OK to inherit from, then use
+//     `wrap_must_use_immediately_t<YourBase>`. It automatically supplies an
+//     `unsafe_mover()` implementation for the wrapped type.
+//
+//   - Derive `public must_use_immediately_crtp<YourType>`. In this case, you
+//     will typically also want to implement the `unsafe_mover()` protocol. To
+//     do so, read the ENTIRE doc of `must_use_immediately_unsafe_mover()`.
+//     Especially: the notes on object slicing, and `noexcept` discipline. Then,
+//     `curried_unsafe_mover_from_bases_and_members()` should be good enough.
+//
+// ## Handling must-use-immediately types in APIs
+//
+// APIs that work with must-use-immediately types must follow these rules:
+//
+// (1) Always take them by-value, never by-reference.
+//
+//     If your API must also support pass-by-reference for other types, then you
+//     must bifurcate it on `must_use_immediately<T>` -- if `true`,
+//     pass-by-value; if `false`, pass-by-reference.
+//
+// (2) NEVER expose non-static member functions for actions that consume the
+//     must-use-immediately value -- `*this` is always by-reference.
+//
+//     INSTEAD: Use pass-by-value static member functions, or ADL CPOs.
+//
+// (3) Must-use-immediately types are immovable, but you may need to move them
+//     internally in your API implementation.  For this, you can use the
+//     callable from `must_use_immediately_unsafe_mover(std::move(t))`. For
+//     example, you can pass `mover` into another coroutine, and then call
+//     `mover()` to reconstitute the value.  The most typical pattern is to
+//     forward a prvalue (by-value) argument by doing both on one line:
+//     must_use_immediately_unsafe_mover(std::move(t))()
+//
+//     DANGER: DO NOT use this to move immovable types outside of your API,
+//     where the lifetime safety was assured via pass-by-value (1) & (2).
+//
+// ## Case study: Must-use-immediately awaitables in `folly::coro`
+//
+// When `must_use_immediately_v<A> == true`, this indicates that the awaitable
+// or semiawaitable `A` should be `co_await`ed in the full-expression that
+// created it.  For an example, see `NowTask.h`.  Using immediate awaitables
+// reduces the risk of lifetime bugs.
+//
+// To support rule (1), many coro APIs bifurcate on `must_use_immediately_v`.
+// This is primarily done to support non-standard awaitables like `coro::Baton`,
+// which must be awaited by-reference. The better solution would be to make
+// `Baton::co_await()` return a must-use-immediately awaitable, and make most
+// coro APIs take (semi)awaitables by-value.
+//
+// To support rule (2), we have been removing non-static member functions for
+// actions that consume the (semi)awaitable.  For example:
+//   - `task.scheduleOn()` was removed in favor of `co_withExecutor()`,
+//   - `.start()` and `.semi()` are unsafe and omitted by `now_task`, etc.
+//
+// Caveat: If you encounter a public `folly::coro` API that does not yet handle
+// must-use-immediately (semi)awaitables, and simply takes the input by `&&`,
+// please fix it via one of these paths:
+//   - If possible, switch it to only take (semi)awaitables by-value.
+//   - If not, branch the API as in (1) above.
+//   - Ask for help in the Coroutines group.
+//
+// ## Why do we need `folly_must_use_immediately_t` member type tags?
+//
+// Isn't immovability (and/or `unsafe_mover()`) enough?
+//   - Not all must-use-immediately types have `unsafe_mover`.  In rare cases,
+//     it is okay if the type cannot be moved.
+//   - Some APIs, especially in `folly::coro`, must allow pass-by-reference. In
+//     those cases, the API must bifurcate on `must_use_immediately_v` so that
+//     use-immediately types are taken by value.
+//   - Finally, the member-type-alias lets us add a (future) lint error whenever
+//     an API takes a must-use-immediately arg, UNLESS it is marked with a
+//     special `clang::annotate_type()` that excuses the bad behavior:
+//     https://clang.llvm.org/docs/AttributeReference.html#annotate-type
+//
+// ## Why is the `unsafe_mover()` protocol required?
+//
+// Without prvalue-forwarding support in the language, it becomes impossible to
+// write code like this:
+//
+//   auto compositeImmediate() { return combine(immediate1(), immediate2()); }
+//
+// The problem is that we cannot move `immediate1()` and `immediate2()` into any
+// larger structure (they are immovable), but we also cannot take their
+// references, since those become invalid upon `return`.
+//
+// In practice, it is essential to be able to write functions that compose
+// must-use-immediately types, and we repeatedly found it necessary to add
+// unsafe movers -- for coro (semi)awaitables, for bindings, and for pipelines.
+
+namespace detail {
+
+template <typename T>
+using must_use_immediately_of_ =
+    // User-defined types will only RARELY need to explicitly provide
+    // `folly_must_use_immediately_t`.  Instead, prefer these:
+    //  - Add via `wrap_must_use_immediately_t` or `must_use_immediately_crtp`.
+    //  - Check its value via `must_use_immediately`.
+    typename T::folly_must_use_immediately_t;
+
+template <typename Void, typename T>
+struct must_use_immediately_ {
+  static_assert(
+      require_sizeof<T>, "`must_use_immediately_t` on incomplete type");
+  using type = std::false_type;
+};
+
+template <>
+struct must_use_immediately_<void, void> {
+  using type = std::false_type;
+};
+
+template <typename T>
+struct must_use_immediately_<void_t<must_use_immediately_of_<T>>, T> {
+  // This could do an "is `T` immovable" check, but the cost/benefit seems low.
+  // Types that compose use-immediately types will automatically be immovable.
+  //
+  // So, an "is immovable" assert would only guard against a user disregarding
+  // the `private`, not using `wrap_must_use_immediately_t` or
+  // `must_use_immediately_crtp`, and instead WRONGLY adding this to the type:
+  //   folly_must_use_immediately_t = std::true_type
+  using type = must_use_immediately_of_<T>;
+};
+
+#if FOLLY_CPLUSPLUS >= 202002L
+namespace must_use_immediately_tuple = folly::detail::lite_tuple;
+#else
+namespace must_use_immediately_tuple = std;
+#endif
+
+} // namespace detail
+
+template <typename T>
+using must_use_immediately_t =
+    typename detail::must_use_immediately_<void, T>::type;
+
+template <typename T>
+inline constexpr bool must_use_immediately_v = must_use_immediately_t<T>::value;
+
+#if defined(__cpp_concepts)
+template <typename T>
+concept must_use_immediately = must_use_immediately_v<T>;
+#endif
+
+// Types using `curried_unsafe_mover_t` need to offer a ctor with this passkey.
+class curried_unsafe_mover_private_t {
+ private:
+  template <typename, typename...>
+  friend class curried_unsafe_mover_t;
+  curried_unsafe_mover_private_t() = default;
+};
+
+// Returned from `unsafe_mover` on typical must-use-immediately types.
+//
+// CONSTRUCTING THIS DIRECTLY IS A LAST RESORT --
+//
+// First, try inheriting `public wrap_must_use_immediately_t<YourClass>`, or
+// `curried_unsafe_mover_from_bases_and_members()`. If these do not fit your
+// usage, at least reimplement their `noexcept` and anti-slicing asserts.
+//
+// See `must_use_immediately_unsafe_mover()` for the docs.  In short, for an
+// `Outer` that is must-use-immediately, this stores the constituent bases and
+// members that are needed to reconstitute `Outer` via `operator()`.
+//
+// This contortion is necessary because prvalue semantics cannot be used in
+// object initializers: "[copy elision only applies] when the object being
+// initialized is known not to be a potentially-overlapping subobject".
+//
+// DANGER: Before returning this class from your `unsafe_mover()`, you must
+// review "A note on object slicing" and make sure your usage isn't affected.
+template <typename Outer, typename... Args>
+class curried_unsafe_mover_t {
+ private:
+  detail::must_use_immediately_tuple::tuple<Args...> tup_;
+
+ public:
+  // AVOID CONSTRUCTING DIRECTLY -- see class doc.
+  //
+  // `Outer*` is just for type deduction and should be `nullptr`.
+  //
+  // IMPORTANT: If you change this to be conditionally `noexcept`, you must
+  // update `curried_unsafe_mover_from_bases_and_members` to check that.
+  explicit curried_unsafe_mover_t(Outer*, Args&&... args) noexcept
+      : tup_{std::move(args)...} {
+    // See must_use_immediately_unsafe_mover docblock
+    static_assert((std::is_nothrow_move_constructible_v<Args> && ...));
+  }
+  // Why have both lvalue-ref and rvalue-ref variants?  Canonically, we'd want
+  // this rvalue-ref qualified, but then if you try to curry more than one arg
+  // via `std::move(mover).template get<I>()`, you will necessarily trigger
+  // use-after-move linters.  So, we support this call style, too:
+  //   std::move(mover.template get<I>())()
+  template <size_t Idx>
+  auto&& get() && noexcept {
+    return std::move(detail::must_use_immediately_tuple::get<Idx>(tup_));
+  }
+  template <size_t Idx>
+  auto& get() & noexcept {
+    return detail::must_use_immediately_tuple::get<Idx>(tup_);
+  }
+  Outer operator()() && noexcept {
+    curried_unsafe_mover_private_t priv;
+    // See must_use_immediately_unsafe_mover docblock
+    static_assert(noexcept(Outer{priv, std::move(*this)}));
+    return Outer{priv, std::move(*this)};
+  }
+};
+
+// Default impl of `must_use_immediately_unsafe_mover` for movable types
+template <typename T>
+struct default_unsafe_mover_t {
+ private:
+  T t_;
+
+ public:
+  explicit default_unsafe_mover_t(T&& t) noexcept : t_(std::move(t)) {
+    // See `must_use_immediately_unsafe_mover` docblock
+    static_assert(std::is_nothrow_move_constructible_v<T>);
+  }
+  T operator()() && noexcept { return std::move(t_); }
+};
+
+class must_use_immediately_private_t;
+
+namespace detail {
+template <typename T>
+using must_use_immediately_detect_unsafe_mover_t = decltype(T::unsafe_mover(
+    std::declval<must_use_immediately_private_t>(), FOLLY_DECLVAL(T&&)));
+} // namespace detail
+
+// CRITICAL: Read the docs on the definition below!
+template <
+    typename T,
+    typename DetectRes = detected_or<
+        default_unsafe_mover_t<T>,
+        detail::must_use_immediately_detect_unsafe_mover_t,
+        T>>
+typename DetectRes::type must_use_immediately_unsafe_mover(T&& t) noexcept;
+
+// Many must-use-immediately types will provide the static member
+// `unsafe_mover(must_use_immediately_private_t, Me&&)`.  This passkey type
+// prevents member name collisions.
+class must_use_immediately_private_t {
+ private:
+  template <typename T, typename DetectRes>
+  friend typename DetectRes::type must_use_immediately_unsafe_mover(
+      T&& t) noexcept;
+};
+
+// Read the top-of-file doc, it's simpler to use `wrap_must_use_immediately_t`.
+//
+// Templated to ensure all bases are distinct in a tower-of-wrappers, like
+// `TryAwaitable<now_task<T>>`.  Repeating a base would break the empty base
+// optimization (EBO).
+template <typename Derived>
+struct must_use_immediately_crtp {
+  using folly_must_use_immediately_t = std::true_type;
+
+  must_use_immediately_crtp() = default;
+  ~must_use_immediately_crtp() = default;
+
+  // Avoid `folly::NonCopyableNonMovable` to avoid breaking EBO.
+  must_use_immediately_crtp(must_use_immediately_crtp&&) = delete;
+  must_use_immediately_crtp& operator=(must_use_immediately_crtp&&) = delete;
+  must_use_immediately_crtp(const must_use_immediately_crtp&) = delete;
+  must_use_immediately_crtp& operator=(const must_use_immediately_crtp&) =
+      delete;
+
+  template <safe_alias>
+  using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;
+};
+
+// BEST FOR TYPICAL USAGE when inheriting `public must_use_immediately_crtp<>`.
+//
+// Creates a `curried_unsafe_mover_t` that decomposes an object into its base
+// classes and member variables, allowing it to be safely moved and later
+// reconstructed. This is the standard implementation pattern for
+// `unsafe_mover()` methods in must-use-immediately types.
+//
+// Invariants:
+//   - `Me` is never const, never an lvalue ref.
+//   - `Me` is a standard-layout class.
+//   - `PtrToMembers` are pointers to member variables of `Me` (or its bases).
+//
+// Pass argument in this order for the "forgot to curry" size heuristic to work:
+//   - `Bases` go in the standard inheritance order: base-to-derived,
+//     left-to-right.
+//   - `PtrToMembers` within a sub-object should be in declaration order, across
+//     sub-objects (uncommon!) in standard inheritance order.
+//
+// WATCH OUT: While construction logic would recommend moving them out in the
+// reverse order of the above, but this is currently not done for lack of need.
+//
+// Design note: I considered eliding `default_unsafe_mover_t` when a base or
+// member is movable.  This would make the mover type signature shorter, at the
+// expense of complicating the uncurrying code.  Since it also doesn't reduce
+// the number of move-copies, I decided against it.
+template <
+    // Mandatory template parameter: The class defining the `unsafe_mover` that
+    // is calling this function. Detects object slicing.
+    typename Caller,
+    typename Me,
+    typename... Bases,
+    // Future: Unfortunately, MSVC2022 does not allow deducing the member-type
+    // and object-type of `PtrToMembers` here, so we have to instantiate
+    // `member_pointer_member_t` below.
+    //
+    // NB: We don't assert the object type of the pointer-to-member.  Although
+    // typically it's just `Me...`, it can differ when packing a member from a
+    // subobject that doesn't provide its own `unsafe_mover`.
+    auto... PtrToMembers,
+    typename RetVal = curried_unsafe_mover_t<
+        Me,
+        decltype(must_use_immediately_unsafe_mover(FOLLY_DECLVAL(Bases)))...,
+        decltype(must_use_immediately_unsafe_mover(FOLLY_DECLVAL(
+            member_pointer_member_t<decltype(PtrToMembers)>)))...>>
+RetVal curried_unsafe_mover_from_bases_and_members(
+    tag_t<Bases...>, vtag_t<PtrToMembers...>, Me&& me) noexcept {
+  static_assert(!std::is_reference_v<Me>);
+  static_assert((!std::is_reference_v<Bases> && ...));
+  static_assert(std::is_convertible_v<Me&&, Caller&&>);
+  // This slicing check is reliable and should remain mandatory. It fires if
+  // `Me` is a derived class of `Caller` that added some data members or other
+  // virtual inheritance. It's very likely that the caller needs to define its
+  // own `unsafe_mover` to avoid object slicing bugs.
+  static_assert(sizeof(Caller) == sizeof(Me));
+  // A second heuristic to help detect when you forgot to curry some parts of
+  // the class.  Since the curried mover is just a tuple, its size will
+  // typically match the size of a "normal" class storing the same data in the
+  // same order. The quotes around "normal" mean it's not an official notion --
+  // this heuristic works for more interesting classes than "standard layout",
+  // but it'll also definitely break if the class uses data packing features, or
+  // virtual inheritance.
+  //
+  // The moment this breaks for a reasonable usage, we should add an opt-out.
+  static_assert(sizeof(Me) == sizeof(RetVal));
+  // NB: Assumes that the `curried_unsafe_mover_t` ctor is `noexcept`, and that
+  // it already asserts that the inputs are nothrow-movable.
+  return curried_unsafe_mover_t{
+      (Me*)nullptr,
+      [&]() -> decltype(auto) {
+        static_assert(noexcept(
+            must_use_immediately_unsafe_mover(static_cast<Bases&&>(me))));
+        return must_use_immediately_unsafe_mover(static_cast<Bases&&>(me));
+      }()...,
+      [&]() -> decltype(auto) {
+        auto&& member = static_cast<Me&&>(me).*PtrToMembers;
+        static_assert(
+            noexcept(must_use_immediately_unsafe_mover(std::move(member))));
+        return must_use_immediately_unsafe_mover(std::move(member));
+      }()...};
+}
+
+template <typename Inner>
+class wrap_must_use_immediately_t
+    : public Inner,
+      public must_use_immediately_crtp<wrap_must_use_immediately_t<Inner>> {
+ private:
+  template <typename T>
+  using my_curried_mover = curried_unsafe_mover_t<
+      T,
+      decltype(must_use_immediately_unsafe_mover(FOLLY_DECLVAL(Inner)))>;
+
+ public:
+  using Inner::Inner;
+
+  // Required for base class disambiguation if a generic `Inner` already
+  // defined `folly_must_use_immediately_t` (e.g. `TaskWrapper.h` sets it to
+  // `false_type` for movable `Inner`).
+  using typename must_use_immediately_crtp<
+      wrap_must_use_immediately_t<Inner>>::folly_must_use_immediately_t;
+
+  // Same disambiguation for `folly_private_safe_alias_t`. Must-use-immediately
+  // types are unsafe since they contain references with unknown lifetimes.
+  template <safe_alias>
+  using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;
+
+  // See the top-of-file docblock before defining your own `unsafe_mover`.
+  template <
+      typename Me, // not a forwarding ref; SFINAE will fail on lval refs
+      // A more conservative design would be to require `is_same_v` here, which
+      // would mean that deriving from a must-use-immediately type requires you
+      // to provide a new `unsafe_mover` (typically a clone of the one in
+      // `wrap_must_use_immediately_t`).
+      //
+      // However, reusing the `unsafe_mover` of a base-class is fairly
+      // low-risk, since we do a simple `sizeof` check for object slicing here.
+      // Undetected problems could only arise if the derived classes adds
+      // mandatory side effects in its destructor / move / assignment.
+      std::enable_if_t<std::is_base_of_v<Inner, Me>, int> = 0>
+  static my_curried_mover<Me> unsafe_mover(
+      must_use_immediately_private_t, Me&& me) noexcept {
+    return curried_unsafe_mover_from_bases_and_members<
+        wrap_must_use_immediately_t>(
+        tag<Inner>, vtag</*no members*/>, static_cast<Me&&>(me));
+  }
+  template <
+      typename DerivedFromMe,
+      // Matches the SFINAE logic in our `unsafe_mover`
+      std::enable_if_t<
+          std::is_base_of_v<wrap_must_use_immediately_t, DerivedFromMe>,
+          int> = 0>
+  explicit wrap_must_use_immediately_t(
+      curried_unsafe_mover_private_t,
+      my_curried_mover<DerivedFromMe>&& mover) noexcept(noexcept(Inner{
+      std::move(mover).template get<0>()()}))
+      : Inner{std::move(mover).template get<0>()()} {}
+};
+
+// When a function takes a must-use-immediately type by-value, it may use the
+// `must_use_immediately_unsafe_mover()` protocol to move it internally.  To
+// retain lifetime-safety, the function MUST NOT allow the value be used outside
+// of its own lifetime. Rationale: the must-use-immediately args may contain
+// references that become invalid at the end of the fn call's full-expression.
+//
+// Wraps `T::unsafe_mover(must_use_immediately_private_t, T&&)` for immovable
+// types that implement it. For movable types that don't support the protocol,
+// falls back to `default_unsafe_mover_t`.
+//
+// ## Implementing `T::unsafe_mover(must_use_immediately_private_t, T&&)`
+//
+// Required semantics:
+//
+//  - `unsafe_mover` is a destructive operation on an immovable type.  Note:
+//    `T&&` is an rvalue ref, not a forwarding one.  Do NOT support lval refs.
+//  - It takes ownership of the internals of `T`, leaving it "moved out".
+//  - It returns a mover value (never a reference), whose `operator() &&` is a
+//    single-use operation returning a prvalue equivalent to the original `T`.
+//  - Both construction and invocation of the returned mover are `noexcept`.
+//
+// Written without constraints, `Base::unsafe_mover()` will also be invocable on
+// derived instances. This is occasionally desirable, but comes with a
+// significant risk of object-slicing bugs (next section), so implementations
+// should lock down the type of `T` as much as practical, via patterns like:
+//
+//   template <typename U, std::enable_if_t<std::is_same_v<U, T>, int> = 0>
+//   static auto unsafe_mover(must_use_immediately_private_t, U&&);
+//
+// ### A note on object slicing when inheriting from must-use-immediately types
+//
+// All `unsafe_mover` implementations work by moving out the constituent parts
+// of the immovable type. Since parts can be must-use-immediately / immovable,
+// we'll usually call `must_use_immediately_unsafe_mover()` on each one.
+//
+// An object slicing problem will occur whenever `Base::unsafe_mover` is called
+// on a `Derived` class with one of these properties:
+//
+// (a) It adds a new member variable, or new base with member variables. Then,
+//     `Base::unsafe_mover` wouldn't know about the new data and discard it.
+//
+// (b) It adds new side effects to destruction, move, copy, or assignment. Then,
+//     the `unsafe_mover()`'s wrapping/unwrapping will cause the custom logic to
+//     run at an unexpected time.
+//
+// Such `Derived` types MUST customize `unsafe_mover` to return a custom mover
+// that accounts for the slicing-unfriendly behaviors (a) or (b).
+//
+// Some slicing-prevention strategies:
+//   - Prefer composition (`Inner inner_;`) over inheritance.
+//   - Prevent `Base::unsafe_mover()` from being used with derived classes via
+//     constraints (example above), by marking the class `final`, or both.
+//   - If you must allow derived classes, then either use
+//     `curried_unsafe_mover_from_bases_and_members()`, which implements a
+//     `sizeof()`-based slicing detection heuristic, or (last resort) review its
+//     comments & implement your own.
+//   - Test, test, test: Whenever you derive from a must-use-immediately class,
+//     round-trip it via an unsafe mover and confirm it works as desired.
+//
+// There is no silver bullet. For example, comparing `sizeof()` can have
+// false-positives (e.g. adding virtual inheritance) and false-negatives (side
+// effects in dtor / move / etc). When inheriting from must-use-immediately,
+// request thoughtful code review, and leave comments pointing at this doc for
+// the next person to touch your code.
+//
+// ## A note on `noexcept` discipline
+//
+// This implementation `static_assert`s that constructing **and** using a mover
+// is `noexcept`. Internally, movers implementations must not neglect to test
+// their own operations via `noexcept()` expressions.
+//
+// In the future this current API could be relaxed to `noexcept(noexcept(...))`,
+// but none of the current use-cases SHOULD have throwing move ctors
+// (`folly::bind` bindings, `folly::coro` awaitables, `folly::coro` pipelines).
+//
+// In the unlikely event we make this change, we'd have to add a new
+// `static_assert` in every place that uses `must_use_immediately_unsafe_mover`
+// to make sure it's still `noexcept` in that usage ...  or just fix it up.
+//
+// So, this is an expensive, breaking change.  But, it is probably not worth it
+// to forward-engineer this, and all its current consumers, to use
+// `noexcept(noexcept())` for the sake of a use-case that may never come.
+//
+// ### Why is `unsafe_mover` a static method?
+//
+// This has two outcomes:
+//   - Each `unsafe_mover` can explicitly decide whether to accept subclasses.
+//     As discussed above, this helps types manage object slicing risk.
+//   - Unimportantly, writing `T::unsafe_mover` **below** is a cheap syntax-only
+//     way of ensuring that `T&&` is NOT a forwarding ref, but an rvalue ref.
+template <typename T, typename DetectRes>
+// DANGER: Passing by `&&` can violate the use-immediately restriction!  The
+// caller MUST have taken `t` by-value to make it OK to call this.
+//
+// NB: `T` is not a forwarding ref, see `static_assert` below.
+typename DetectRes::type must_use_immediately_unsafe_mover(T&& t) noexcept {
+  static_assert(!std::is_reference_v<T>);
+  static_assert(noexcept(FOLLY_DECLVAL(typename DetectRes::type&&)()));
+  if constexpr (DetectRes::value_t::value) {
+    must_use_immediately_private_t priv;
+    static_assert(noexcept(T::unsafe_mover(priv, static_cast<T&&>(t))));
+    static_assert(
+        std::is_same_v<
+            T,
+            decltype(T::unsafe_mover(priv, static_cast<T&&>(t))())>,
+        "The result of unsafely moving a T must be exactly T to ensure that we "
+        "satisfy the preconditions for mandatory copy elision, which eliminates "
+        "the possibility of exceptions during conversion");
+    return T::unsafe_mover(priv, static_cast<T&&>(t));
+  } else {
+    static_assert(
+        std::is_nothrow_constructible_v<default_unsafe_mover_t<T>, T&&>);
+    return default_unsafe_mover_t<T>{static_cast<T&&>(t)};
+  }
+}
+
+} // namespace folly::ext
+
+FOLLY_POP_WARNING
diff --git a/folly/folly/lang/New.h b/folly/folly/lang/New.h
--- a/folly/folly/lang/New.h
+++ b/folly/folly/lang/New.h
@@ -71,23 +71,23 @@
 
 //  operator_new
 struct operator_new_fn {
-  FOLLY_NODISCARD FOLLY_ERASE void* operator()( //
+  [[nodiscard]] FOLLY_ERASE void* operator()( //
       std::size_t const s) const //
       noexcept(noexcept(::operator new(0))) {
     return detail::op_new_<true>(s);
   }
-  FOLLY_NODISCARD FOLLY_ERASE void* operator()( //
+  [[nodiscard]] FOLLY_ERASE void* operator()( //
       std::size_t const s,
       std::nothrow_t const& nt) const noexcept {
     return detail::op_new_<true>(s, nt);
   }
-  FOLLY_NODISCARD FOLLY_ERASE void* operator()( //
+  [[nodiscard]] FOLLY_ERASE void* operator()( //
       std::size_t const s,
       std::align_val_t const a) const //
       noexcept(noexcept(::operator new(0))) {
     return detail::op_new_<detail::cpp_aligned_new_>(s, a);
   }
-  FOLLY_NODISCARD FOLLY_ERASE void* operator()( //
+  [[nodiscard]] FOLLY_ERASE void* operator()( //
       std::size_t const s,
       std::align_val_t const a,
       std::nothrow_t const& nt) const noexcept {
diff --git a/folly/folly/lang/SafeAlias-fwd.h b/folly/folly/lang/SafeAlias-fwd.h
--- a/folly/folly/lang/SafeAlias-fwd.h
+++ b/folly/folly/lang/SafeAlias-fwd.h
@@ -19,7 +19,7 @@
 #include <algorithm>
 #include <type_traits>
 
-/// See `folly/coro/safe/SafeAlias.h` for the high-level docs & definitions.
+/// See `folly/lang/SafeAlias.h` for the high-level docs & definitions.
 ///
 /// This is a MINIMAL header to let types declare their `safe_alias` level.
 ///
@@ -47,16 +47,16 @@
   // Definitely has aliasing, we know nothing of the lifetime.
   unsafe,
   // Implementation detail of `async_closure`, used for creating
-  // `ClosureTask`, a restricted-usage `SafeTask`.  Other code should treat
-  // this as `unsafe`.  `SafeTask.h` & `Captures.h` explain the rationale.
+  // `closure_task`, a restricted-usage `safe_task`.  Other code should treat
+  // this as `unsafe`.  `safe_task.h` & `Captures.h` explain the rationale.
   unsafe_closure_internal,
   // Implementation detail of `async_closure`, used for creating
-  // `MemberTask`, a restricted-usage `SafeTask`.  Other code should treat
+  // `member_task`, a restricted-usage `safe_task`.  Other code should treat
   // this as `unsafe`.  Closure-related code that distinguishes this from
   // `unsafe_closure_internal` expects this value to be higher.
   unsafe_member_internal,
   // Used only in `async_closure*()` -- the minimum level it considers safe
-  // for arguments, and the minimum level of `SafeTask` it will emit.
+  // for arguments, and the minimum level of `safe_task` it will emit.
   //   - Represents an arg that can schedule a cleanup callback on an
   //     ancestor's cleanup arg `A`.  This safety level cannot be stronger
   //     than `after_cleanup_ref` because otherwise such a ref could be
@@ -101,12 +101,12 @@
 
 // IMPORTANT:
 //
-// (1) Do NOT move the primary, permissive template in here!
+// (1) Do NOT move the primary template in here!
 //
 //     The reason is that we REQUIRE the extra specializations in `SafeAlias.h`
-//     before concluding that something is safe.  It would be very bad if a
-//     user forgot to include `SafeAlias.h`, and was told that a type was safe,
-//     when it isn't.
+//     before allowing `lenient_safe_alias_v` to conclude that something is
+//     safe.  It would be very bad if a user forgot to include `SafeAlias.h`,
+//     and was told that a type was safe, when it isn't.
 //
 // (2) Keep this SMALL -- pretty much everything in `folly/coro` includes this.
 //
@@ -114,23 +114,28 @@
 //     check is: if you have to add an `#include`, it goes in `SafeAlias.h`.
 //
 // Future: Replace the SFINAE arg with `requires` once on C++20.
-template <typename T, typename /*SFINAE*/ = void>
+template <typename T, safe_alias /*default*/, typename /*SFINAE*/ = void>
 struct safe_alias_of;
 
 // `const` and `volatile` qualifiers don't affect the `safe_alias` measurement.
-template <typename T>
-struct safe_alias_of<const T> : safe_alias_of<T> {};
-template <typename T>
-struct safe_alias_of<volatile T> : safe_alias_of<T> {};
+template <typename T, safe_alias Default>
+struct safe_alias_of<const T, Default> : safe_alias_of<T, Default> {};
+template <typename T, safe_alias Default>
+struct safe_alias_of<volatile T, Default> : safe_alias_of<T, Default> {};
 
 // Raw references & pointers are `unsafe`.
-template <typename T>
-struct safe_alias_of<T*> : safe_alias_constant<safe_alias::unsafe> {};
-template <typename T>
-struct safe_alias_of<T&> : safe_alias_constant<safe_alias::unsafe> {};
-template <typename T>
-struct safe_alias_of<T&&> : safe_alias_constant<safe_alias::unsafe> {};
+template <typename T, safe_alias Default>
+struct safe_alias_of<T*, Default> : safe_alias_constant<safe_alias::unsafe> {};
+template <typename T, safe_alias Default>
+struct safe_alias_of<T&, Default> : safe_alias_constant<safe_alias::unsafe> {};
+template <typename T, safe_alias Default>
+struct safe_alias_of<T&&, Default> : safe_alias_constant<safe_alias::unsafe> {};
 
+// `void` is incomplete and would fail the primary template
+template <safe_alias Default>
+struct safe_alias_of<void, Default>
+    : safe_alias_constant<safe_alias::maybe_value> {};
+
 // Most `folly` types annotate their safety via a member type alias. We do
 // this not just because it's shorter, but also because member classes, like
 // AsyncGenerator<>::NextAwaitable, are non-deducible, and thus cannot be
@@ -138,19 +143,35 @@
 //
 // This is in `SafeAlias-fwd.h`, since that allows the various task wrappers
 // NOT to include the larger `SafeAlias.h`.
-template <typename T>
-struct safe_alias_of<T, std::void_t<typename T::folly_private_safe_alias_t>>
-    : T::folly_private_safe_alias_t {};
+template <typename T, safe_alias Default>
+struct safe_alias_of<
+    T,
+    Default,
+    std::void_t<typename T::template folly_private_safe_alias_t<Default>>>
+    : T::template folly_private_safe_alias_t<Default> {};
 
+// Use this in APIs that take callable objects, since lambda captures present a
+// particularly high risk for aliasing bugs. If you have a compile error:
+//   - For regular lambdas with captures, use `safe_closure()`.
+//   - For custom callables, the best solution is to add the correct
+//     `folly_private_safe_alias_t` to your type.
+//   - For async coroutines, use `async_closure()` or `safe_task.h`.
+//   - For one-offs, `SafeAlias.h`  includes some `manual_safe_*` workarounds.
+//     You MUST include a comment that proves your usage is safe.
 template <typename T>
-inline constexpr safe_alias safe_alias_of_v = safe_alias_of<T>::value;
+inline constexpr safe_alias strict_safe_alias_of_v =
+    safe_alias_of<T, safe_alias::unsafe>::value;
+// For low-risk APIs, where "vanilla C++ usability" outweighs safety.
+template <typename T>
+inline constexpr safe_alias lenient_safe_alias_of_v =
+    safe_alias_of<T, safe_alias::maybe_value>::value;
 
 // Utility for computing the `safe_alias_of` a composite type.
 // Returns the lowest `safe_alias` level of all the supplied `Ts`.
-template <typename... Ts>
+template <safe_alias Default, typename... Ts>
 struct safe_alias_of_pack {
   static constexpr auto value =
-      std::min({safe_alias::maybe_value, safe_alias_of_v<Ts>...});
+      std::min({safe_alias::maybe_value, safe_alias_of<Ts, Default>::value...});
 };
 
 } // namespace folly
diff --git a/folly/folly/lang/SafeAlias.h b/folly/folly/lang/SafeAlias.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/SafeAlias.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Traits.h>
+#include <folly/lang/SafeAlias-fwd.h>
+
+#include <type_traits>
+
+namespace folly {
+template <typename> // Forward-decl to avoid `RValueReferenceWrapper.h` dep
+class rvalue_reference_wrapper;
+} // namespace folly
+
+/*
+"Aliasing" is indirect access to memory via pointers or references.  It is
+the major cause of memory-safety bugs in C++, but is also essential for
+writing correct & performant C++ programs.  Fortunately,
+  - Much business logic can be written in a pure-functional style, where
+    only value semantics are allowed.  Such code is easier to understand,
+    and has much better memory-safety.
+  - When references ARE used, the most common scenario is passing a
+    reference from a parent lexical scope to descendant scopes.
+
+`strict_safe_alias_of_v` / `lenient_safe_alias_of_v` are _heuristics_ to check
+whether a type is likely to be memory-safe in the above settings.  The
+`safe_alias` enum shows a hierarchy of memory safety, but you only need to know
+about two:
+  - `unsafe` -- e.g. raw pointers or references, and
+  - `maybe_value` -- `int`, `std::pair<int, char>`, or `std::unique_ptr<Foo>`.
+
+A user can easily bypass the heuristic -- since C++ lacks full reflection,
+it is impossible to make this bulletproof.  Our goals are much more modest:
+  - Make unsafe aliasing **more** visible in code review, and
+  - Encourage programmers to use safe semantics by default.
+
+The BIG CAVEATS are:
+
+ - The "composition hole" -- i.e. aliasing hidden in structures.  We can't see
+   unsafe class members, so `UnsafeStruct` below will be deduced to have
+   `maybe_value` safety unless you specify a `safe_alias` value:
+     struct UnsafeStruct {
+       int* rawPtr;
+       // Without this, most APIs will treat `UnsafeStruct` as `maybe_value`
+       template <safe_alias>
+       using folly_private_safe_alias_t =
+           safe_alias_constant<safe_alias::unsafe>;
+     };
+   Future: Perhaps with C++26 reflection, this could be fixed.
+
+   The "lambda hole" is a particularly easy instance of the "composition hole".
+   With lambda captures, a parent needs just one `&` to let a child pass a
+   soon-to-be-dangling reference up the stack.  E.g. this compiles:
+       int* badPtr;
+       auto t = async_closure(
+         // LAMBDA HOLE: We can't tell this callable object is unsafe!
+         bind::args{[&](int p) { *badPtr = p; }},
+         [](auto fn) -> closure_task<void> {
+           int i = 5;
+           fn(i); // FAILURE: Dereferencing uninitialized `badPtr`.
+           co_return;
+         });
+
+ - Nullability & pointer stability: These hazards are not very specific to
+   coroutines, and the current design of `folly/coro/safe` largely avoids
+   unstable containers.  Nonetheless, you must beware container mutation is
+   an easy way to invalidate `safe_alias` memory-safety measurements.  For
+   example `unique_ptr<int>` and `vector<int>` have `maybe_value` safety.
+   However, if you mutate them (`reset()`, `clear()`, etc), that would
+   invalidate any async references (e.g.  `Captures.h`) pointing inside.
+   Luckily, there's no implicit way of getting a safe reference to inside
+   regular containers.  However, it is recommended to reduce accidental
+   nullability where possible.  For example, `capture<unique_ptr<T>>`
+   exposes `reset()`, but `capture_indirect<unique_ptr<T>>` hides it behind
+   `get_underlying_unsafe()`.  Better yet, `capture<AsyncObjectPtr<T>>`
+   blocks the underlying `clear()` method entirely.
+
+If you need to bypass this control, prefer the `manual_safe_*` wrappers
+below, instead of writing a custom workaround.  Always explain why it's safe.
+
+To teach `safe_alias_of` about your type, include `SafeAlias-fwd.h` and either:
+ 1) Add a member type alias to your class:
+    template <safe_alias Default>
+    using using folly_private_safe_alias_t = safe_alias_of<Inner, Default>;
+ 2) Specialize `folly::safe_alias_of<YourT>`.
+
+When adding `safe_alias` annotations to types, stick to these principles:
+  - Always mark the `safe_alias` level in the header that declares your type.
+    For `std` types you cannot change, add the specialization here, in
+    `SafeAlias.h`.  Since we cannot forward-declare from `std`, this
+    unfortunately imposes a tradeoff between build cost and safety.  Commonly
+    used containers are worth the cost.  For less-commonly used containers, we
+    could develop a multi-header setup, plus some linter coverage to ensure the
+    right headers ultimately do get included.
+  - Only use `maybe_value` if your type ACTUALLY follows value semantics.
+  - Unless you're implementing an `async_closure`-integrated type, it is VERY
+    unlikely that you should use anything besides `unsafe` or `maybe_value`.
+  - Use `safe_alias_of_pack` to aggregate safety for a multi-part type.
+*/
+namespace folly {
+
+// The default safety measurements for types that don't specify one.  Note that
+// `SafeAlias-fwd.h` already marks raw pointers & refs as `unsafe`, and peels
+// off CV qualifiers from the type being tested.
+//
+// See also: `strict_safe_alias_of_v` / `lenient_safe_alias_of_v`.
+//
+// As explained in `SafeAlias-fwd.h`, do NOT move this to the `fwd` header.
+// This is commonly used with `lenient_safe_alias_of_v`, so the default
+// behavior is permissive.  Therefore, to guarantee safety, this primary
+// template must be colocated with the other specializations below.
+template <typename T, safe_alias Default, typename /*SFINAE*/>
+struct safe_alias_of
+    : safe_alias_constant<
+          // Stateless & fundamental types are always safe -- `void` is covered
+          // in `SafeAlias-fwd.h`.  Function pointers are covered below, on the
+          // the potentially incorrect theory that adding 2 specializations is
+          // cheaper to compile than this test here:
+          //   std::is_pointer_v<T> &&
+          //   std::is_function_v<std::remove_pointer_t<T>>
+          //
+          // Check `sizeof()` to avoid `is_empty` UB on incomplete types.
+          (std::is_fundamental_v<T> || (sizeof(T) >= 0 && std::is_empty_v<T>))
+              ? safe_alias::maybe_value
+              : Default> {};
+
+// Reference wrappers are unsafe.
+template <typename T, safe_alias Default>
+struct safe_alias_of<std::reference_wrapper<T>, Default>
+    : safe_alias_constant<safe_alias::unsafe> {};
+template <typename T, safe_alias Default>
+struct safe_alias_of<folly::rvalue_reference_wrapper<T>, Default>
+    : safe_alias_constant<safe_alias::unsafe> {};
+
+// Function pointers are stateless, and thus also safe (see primary template).
+template <typename R, typename... Args, safe_alias Default>
+struct safe_alias_of<R (*)(Args...), Default>
+    : safe_alias_constant<safe_alias::maybe_value> {};
+template <typename R, typename... Args, safe_alias Default>
+struct safe_alias_of<R (*)(Args...) noexcept, Default>
+    : safe_alias_constant<safe_alias::maybe_value> {};
+
+// Let `safe_alias_of` recursively inspect `std` containers that are likely to
+// be involved in bugs.  If you encounter a memory-safety issue that would've
+// been caught by this, feel free to extend this.
+template <typename... As, safe_alias Default>
+struct safe_alias_of<std::tuple<As...>, Default>
+    : safe_alias_of_pack<Default, As...> {};
+template <typename... As, safe_alias Default>
+struct safe_alias_of<std::pair<As...>, Default>
+    : safe_alias_of_pack<Default, As...> {};
+template <typename... As, safe_alias Default>
+struct safe_alias_of<std::vector<As...>, Default>
+    : safe_alias_of_pack<Default, As...> {};
+
+// Recursing into `tag_t<>` type lists is nice for metaprogramming
+template <typename... As, safe_alias Default>
+struct safe_alias_of<::folly::tag_t<As...>, Default>
+    : safe_alias_of_pack<Default, As...> {};
+
+// IMPORTANT: If you use the `manual_safe_` escape-hatch wrappers, you MUST
+// comment with clear proof of WHY your usage is safe.  The goal is to
+// ensure careful review of such code.
+//
+// Careful: With the default `Safety`, the contained value or reference can be
+// passed anywhere -- the wrapper pretends to be a value type.
+//
+// If you know a more restrictive safety level for your ref, annotate it to
+// improve safety:
+//  - `after_cleanup_ref` for things owned by co_cleanup args of this closure,
+//  - `co_cleanup_safe_ref` for refs to non-cleanup args owned by this closure,
+//    or any ancestor closure.
+//
+// The types are public since they may occur in user-facing signatures.
+
+template <safe_alias S, typename T>
+struct manual_safe_ref_t : std::reference_wrapper<T> {
+  using typename std::reference_wrapper<T>::type;
+  using std::reference_wrapper<T>::reference_wrapper;
+
+  template <safe_alias>
+  using folly_private_safe_alias_t = safe_alias_constant<S>;
+};
+
+template <safe_alias S, typename T>
+struct manual_safe_val_t {
+  using type = T;
+
+  template <safe_alias>
+  using folly_private_safe_alias_t = safe_alias_constant<S>;
+
+  template <typename... Args>
+  explicit manual_safe_val_t(Args&&... args)
+      : t_(static_cast<Args&&>(args)...) {}
+  template <typename Fn>
+  manual_safe_val_t(std::in_place_type_t<T>, Fn fn) : t_(fn()) {}
+
+  T& get() & noexcept { return t_; }
+  operator T&() & noexcept { return t_; }
+  const T& get() const& noexcept { return t_; }
+  operator const T&() const& noexcept { return t_; }
+  T&& get() && noexcept { return std::move(t_); }
+  operator T&&() && noexcept { return std::move(t_); }
+
+ private:
+  T t_;
+};
+
+template <safe_alias Safety = safe_alias::maybe_value, typename T = void>
+manual_safe_ref_t<Safety, T> manual_safe_ref(T& t) {
+  return manual_safe_ref_t<Safety, T>{t};
+}
+template <safe_alias Safety = safe_alias::maybe_value, typename T>
+manual_safe_val_t<Safety, T> manual_safe_val(T t) {
+  return manual_safe_val_t<Safety, T>{std::move(t)};
+}
+template <safe_alias Safety = safe_alias::maybe_value, typename Fn>
+auto manual_safe_with(Fn&& fn) {
+  using FnRet = decltype(static_cast<Fn&&>(fn)());
+  return manual_safe_val_t<Safety, FnRet>{
+      std::in_place_type<FnRet>, static_cast<Fn&&>(fn)};
+}
+
+template <safe_alias S, typename Fn>
+class manual_safe_callable_t {
+ private:
+  Fn fn_;
+
+ public:
+  template <safe_alias>
+  using folly_private_safe_alias_t = safe_alias_constant<S>;
+
+  explicit manual_safe_callable_t(Fn fn) : fn_(std::move(fn)) {}
+
+  template <typename... Args>
+  auto operator()(Args&&... args) &
+      // Same as FOLLY_DETAIL_FORWARD_BODY, but we don't yet depend on Utility.h
+      noexcept(noexcept(fn_(static_cast<Args&&>(args)...)))
+          -> decltype(fn_(static_cast<Args&&>(args)...)) {
+    return fn_(static_cast<Args&&>(args)...);
+  }
+
+  template <typename... Args>
+  auto operator()(Args&&... args) const& noexcept(
+      noexcept(std::as_const(fn_)(static_cast<Args&&>(args)...)))
+      -> decltype(std::as_const(fn_)(static_cast<Args&&>(args)...)) {
+    return std::as_const(fn_)(static_cast<Args&&>(args)...);
+  }
+
+  template <typename... Args>
+  auto operator()(Args&&... args) && noexcept(
+      noexcept(std::move(fn_)(static_cast<Args&&>(args)...)))
+      -> decltype(std::move(fn_)(static_cast<Args&&>(args)...)) {
+    return std::move(fn_)(static_cast<Args&&>(args)...);
+  }
+};
+
+// Whenever possible, prefer `safe_closure`, since it automatically measures
+// the safety of its stored arguments.
+//
+// Motivation: APIs taking callables should typically use
+// `strict_safe_alias_of_v`, due to the risk posed by lambda captures.
+template <
+    safe_alias Safety = safe_alias::maybe_value,
+    typename Fn,
+    typename UncvrefFn = remove_cvref_t<Fn>>
+manual_safe_callable_t<Safety, UncvrefFn> manual_safe_callable(Fn&& fn) {
+  return manual_safe_callable_t<Safety, UncvrefFn>{static_cast<Fn&&>(fn)};
+}
+
+} // namespace folly
diff --git a/folly/folly/lang/SafeAssert.cpp b/folly/folly/lang/SafeAssert.cpp
--- a/folly/folly/lang/SafeAssert.cpp
+++ b/folly/folly/lang/SafeAssert.cpp
@@ -53,8 +53,7 @@
 //    echo "#endif"
 //  done
 
-#define FOLLY_DETAIL_ERROR(name) \
-  { name, #name }
+#define FOLLY_DETAIL_ERROR(name) {name, #name}
 constexpr std::pair<int, const char*> errors[] = {
 #if defined(E2BIG)
     FOLLY_DETAIL_ERROR(E2BIG),
@@ -498,7 +497,7 @@
 
 #endif
 
-#if !defined(_WIN32) && !defined(_POSIX_FSYNC)
+#if !defined(_WIN32) && !defined(_POSIX_FSYNC) || defined(__XTENSA__)
 
 int fsync(int fh) {
   return 0;
diff --git a/folly/folly/lang/SafeAssert.h b/folly/folly/lang/SafeAssert.h
--- a/folly/folly/lang/SafeAssert.h
+++ b/folly/folly/lang/SafeAssert.h
@@ -131,7 +131,7 @@
 
 template <typename... A>
 safe_assert_msg_type_s<decltype(safe_assert_msg_types_one((A)A{}))::value...>
-safe_assert_msg_types_seq_of(A...); // only used in unevaluated contexts
+    safe_assert_msg_types_seq_of(A...); // only used in unevaluated contexts
 
 template <typename>
 struct safe_assert_msg_types;
diff --git a/folly/folly/lang/SafeClosure.h b/folly/folly/lang/SafeClosure.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/SafeClosure.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/detail/tuple.h>
+#include <folly/lang/SafeAlias.h>
+#include <folly/lang/bind/AsArgument.h>
+#include <folly/lang/bind/Bind.h>
+#include <folly/lang/bind/ToStorage.h>
+
+///
+/// See `SafeClosure.md` for more details.
+///
+/// `safe_closure()` reimplements a lambda-with-value-captures, exposing an API
+/// analogous to Python's `functools.partial`.  These are better than a regular
+/// lambda when you need to pass a callable into a `safe_alias`-aware API, such
+/// as those in `folly/coro/safe`.
+///
+/// `safe_closure()` takes a *safe* callable -- a function pointer, a lambda
+/// without captures, an empty callable class, or a type `T` with
+///   strict_safe_alias_of_v<T> >= safe_alias::closure_min_arg_safety
+///
+/// Then, it measures the `safe_alias_of` its stored arguments, and marks the
+/// returned callable accordingly.  This enables safe APIs to catch many
+/// lifetime-safety bugs at compile time.
+///
+/// Passing an unsafe function or arguments is a compile error.  That's because
+/// if you need an unsafe closure, a lambda with captures will do.
+///
+
+FOLLY_PUSH_WARNING
+FOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS
+
+namespace folly {
+
+namespace detail {
+
+// Captures.h specializes this template to teach safe_closure to store
+// capture<V> types as capture<V&>, without taking a direct dependency.
+template <typename ST>
+struct safe_closure_arg_storage_helper {
+  // If we can't see the type, we probably can't see its specialization either.
+  static_assert(require_sizeof<ST> >= 0);
+  using type = ST;
+};
+
+// Transform a `binding_list_t` to `safe_closure` type parameters:
+//   (1) a `vtag_t` of the input `bind_info_t`s, for use in `operator()`
+//   (2) a tuple of by-value stored args.
+// Constrain the type of `BI` to avoid slicing.
+template <std::same_as<bind::ext::bind_info_t> auto... BIs, typename... BindTs>
+auto deduce_safe_closure_params(
+    tag_t<bind::ext::binding_t<BIs, BindTs>...> bindList)
+    -> tag_t<
+        vtag_t<BIs...>,
+        lite_tuple::tuple<
+            // This helper is a no-op for all types except `capture<Value>`,
+            // for which it causes the ctor to perform an implicit conversion.
+            typename safe_closure_arg_storage_helper<
+                typename bind::ext::bind_to_storage_policy<
+                    // Make fake `binding_t`s to always store args "by mutable
+                    // value" -- storing refs would make the closure unsafe.
+                    bind::ext::binding_t<bind::ext::bind_info_t{}, BindTs>>::
+                    storage_type>::type...>>;
+} // namespace detail
+
+template <typename, typename>
+class safe_closure;
+
+template <
+    typename Fn,
+    // Constrain the type of `BI` to avoid slicing.
+    std::same_as<bind::ext::bind_info_t> auto... ArgBIs,
+    typename ArgsTup>
+class safe_closure<Fn, tag_t<vtag_t<ArgBIs...>, ArgsTup>> {
+ private:
+  // This includes stateless, or explicitly safe functions.  The latter permits
+  // nesting of `safe_closure`s.
+  static_assert(
+      strict_safe_alias_of_v<Fn> >= safe_alias::closure_min_arg_safety);
+  static_assert(
+      lenient_safe_alias_of_v<ArgsTup> >= safe_alias::closure_min_arg_safety);
+
+  Fn fn_;
+  ArgsTup argsTup_;
+
+ public:
+  safe_closure(auto bargs, Fn fn)
+      : fn_{std::move(fn)}, argsTup_{[&bargs] {
+          // This `apply` is here because `lite_tuple` currently lacks
+          // converting constructors.
+          return detail::lite_tuple::apply(
+              [](auto&&... as) {
+                return ArgsTup{static_cast<decltype(as)>(as)...};
+              },
+              std::move(bargs).unsafe_tuple_to_bind());
+        }()} {}
+
+  auto operator()(auto&&... unboundArgs) & {
+    return detail::lite_tuple::apply(
+        [&](auto&... as) {
+          return fn_(
+              bind::ext::bind_as_argument<ArgBIs>(as)...,
+              static_cast<decltype(unboundArgs)>(unboundArgs)...);
+        },
+        argsTup_);
+  }
+
+  auto operator()(auto&&... unboundArgs) const& {
+    return detail::lite_tuple::apply(
+        [&](const auto&... as) {
+          return std::as_const(fn_)(
+              bind::ext::bind_as_argument<ArgBIs>(as)...,
+              static_cast<decltype(unboundArgs)>(unboundArgs)...);
+        },
+        argsTup_);
+  }
+
+  auto operator()(auto&&... unboundArgs) && {
+    return detail::lite_tuple::apply(
+        [&](auto&&... as) {
+          return std::move(fn_)(
+              bind::ext::bind_as_argument<ArgBIs>(
+                  static_cast<decltype(as)>(as))...,
+              static_cast<decltype(unboundArgs)>(unboundArgs)...);
+        },
+        std::move(argsTup_));
+  }
+
+  // Use lenient safety for the stored arguments since we have no a priori
+  // expectation of them being high-risk. Contrast with this recursive form:
+  //   safe_alias_of_pack<Default, Fn, ArgsTup>
+  // With that, passing `SomeArg` with no safety marking would make the whole
+  // closure strict-unsafe, which would degrade usability.
+  template <safe_alias>
+  using folly_private_safe_alias_t = safe_alias_constant<std::min(
+      strict_safe_alias_of_v<Fn>, lenient_safe_alias_of_v<ArgsTup>)>;
+};
+
+template <typename BindArgs, typename Fn>
+safe_closure(BindArgs, Fn) -> safe_closure<
+    Fn,
+    decltype(detail::deduce_safe_closure_params(
+        typename BindArgs::binding_list_t{}))>;
+
+} // namespace folly
+
+FOLLY_POP_WARNING
diff --git a/folly/folly/lang/Strong.h b/folly/folly/lang/Strong.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/Strong.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iosfwd>
+#include <type_traits>
+#include <utility>
+
+#include <fmt/format.h>
+
+namespace folly {
+
+/// `strong<T, Tag>` - prevent accidental mixing of semantically different uses
+/// of the same underlying type. Known as "newtype" in some languages. C++20.
+/// Default-construction value-initializes `T`, even if it's a primitive type.
+///
+/// The tag type should be the name of your derived type. This guarantees that
+/// different strong `T`s are not interchangeable.
+///
+/// The `std::hash`, `fmt::format`, and `operator<<(ostream&)` implementations
+/// rely on this convention, so if you see failures in those, check your tag.
+/// You can customize the output per-type, see `CustomOutput` in the test.
+///
+/// It is often OK to allow implicit construction from the underlying type.
+/// We strongly recommend using concepts to limit undesired conversions:
+///
+///   struct UserId : public strong<uint64_t, UserId> {
+///     using strong<uint64_t, UserId>::strong;
+///     // Prohibit risky signed-unsigned & float-int conversions.
+///     // Callers will want to suffix integer literals with `u`.
+///     /* implicit */ UserId(std::unsigned_integral auto n) : strong{n} {}
+///   };
+///
+///   UserId uid{123u}; // OK, explicit construction
+///   UserId uid = 123u; // OK, implicit conversion
+///   UserId uid{123}; // Warning: narrowing conversion
+///   UserId uid = 123; // Error: no viable conversion
+///
+/// For ease of use, even without an implicit constructor, your strong type
+/// will be comparable with the underlying type.
+template <typename T, typename /* your derived class */>
+class strong {
+ private:
+  static_assert(!std::is_reference_v<T>);
+  T value_{};
+
+ public:
+  using underlying_type = T;
+
+  constexpr strong() = default; // note: value-initializes `T`, even if trivial
+
+  // When constructed explicitly, forward args to the underlying ctor.
+  // In particular, this will construct `strong` from `T`.
+  template <typename Head, typename... Tail>
+    requires(
+        std::is_constructible_v<T, Head, Tail...> &&
+        // Avoid touching the standard ctors
+        !std::derived_from<std::remove_cvref_t<Head>, strong>)
+  explicit constexpr strong(Head&& head, Tail&&... tail)
+      : value_{std::forward<Head>(head), std::forward<Tail>(tail)...} {}
+
+  // Comparable iff the underlying type is.
+  friend bool operator==(const strong&, const strong&) = default;
+  friend auto operator<=>(const strong&, const strong&) = default;
+
+  // For ease of use, `strong` is comparable with `underlying_type`, even if
+  // your derived class does not add an implicit constructor. Accept only `T` to
+  // prevent implicit conversions from other types (`strong<int>` vs `double`).
+  template <typename U>
+    requires std::same_as<std::remove_cvref_t<U>, T>
+  friend constexpr bool operator==(const strong& st, U&& underlying) {
+    return st.value_ == underlying;
+  }
+  template <typename U>
+    requires std::same_as<std::remove_cvref_t<U>, T>
+  friend constexpr auto operator<=>(const strong& st, U&& underlying) {
+    return st.value_ <=> underlying;
+  }
+
+  // Explicitly convertible to the underlying type
+  explicit constexpr operator T&() & { return value_; }
+  explicit constexpr operator T const&() const& { return value_; }
+  explicit constexpr operator T&&() && { return std::move(value_); }
+  explicit constexpr operator T const&&() const&& { return std::move(value_); }
+
+  // Access the underlying type
+  constexpr T& value() & { return value_; }
+  constexpr const T& value() const& { return value_; }
+  constexpr T&& value() && { return std::move(value_); }
+  constexpr const T&& value() const&& { return std::move(value_); }
+};
+
+/// `strong_derefable<T, Tag>` - a strong type with pointer-like dereference
+/// operators. Inherits all functionality from `strong` and adds `operator*` and
+/// `operator->` for convenient access to the underlying value. Usage:
+///
+///   struct X : strong_derefable<T, X> { /* as above */ };
+template <typename T, typename Tag>
+class strong_derefable : public strong<T, Tag> {
+ public:
+  using strong<T, Tag>::strong;
+
+  constexpr const T& operator*() const& { return this->value(); }
+  constexpr T& operator*() & { return this->value(); }
+  constexpr const T&& operator*() const&& { return std::move(*this).value(); }
+  constexpr T&& operator*() && { return std::move(*this).value(); }
+
+  constexpr const T* operator->() const { return &this->value(); }
+  constexpr T* operator->() { return &this->value(); }
+};
+
+// Strong types support `ostream` iff the underlying type supports it.
+template <typename T, typename Tag>
+auto operator<<(std::ostream& os, const strong<T, Tag>& st)
+    -> decltype(os << st.value()) {
+  return os << st.value();
+}
+
+template <typename T>
+concept is_strong =
+    std::derived_from<T, strong<typename T::underlying_type, T>>;
+
+} // namespace folly
+
+// Let strong types be keys in unordered containers. A strong type is
+// hashable iff the underlying type is.
+template <typename T>
+  requires(
+      folly::is_strong<T> &&
+      // Required for `folly::is_hashable_v` to be correct.
+      requires(typename T::underlying_type underlying) {
+        {
+          std::hash<typename T::underlying_type>{}(underlying)
+        } -> std::convertible_to<std::size_t>;
+      })
+struct std::hash<T> {
+  size_t operator()(const T& st) const {
+    return std::hash<typename T::underlying_type>{}(st.value());
+  }
+};
+
+// Strong types are formattable iff the underlying type is.
+template <typename T>
+  requires(
+      folly::is_strong<T> &&
+      fmt::is_formattable<typename T::underlying_type>::value)
+struct fmt::formatter<T> : fmt::formatter<typename T::underlying_type> {
+  auto format(const T& st, auto& ctx) const {
+    return fmt::formatter<typename T::underlying_type>::format(st.value(), ctx);
+  }
+};
diff --git a/folly/folly/lang/Switch.h b/folly/folly/lang/Switch.h
--- a/folly/folly/lang/Switch.h
+++ b/folly/folly/lang/Switch.h
@@ -54,15 +54,15 @@
  *
  * When you switch on the value of a concretely defined enum with a very large
  * set of values, and only need to address a sane subset of the values, you
- * write a flexible switch. Additionally, when you switch on the a non-enum
- * value (like an int), you write a flexible switch.
+ * write a non-exhaustive switch.  Additionally, when you switch on the a
+ * non-enum value (like an int), you write a non-exhaustive switch.
  *
  *    enum class Color {
  *      // every color in a 64 color palette named as an enum value
  *    };
  *
  *    bool isRedColor(Color c) {
- *      FOLLY_FLEXIBLE_SWITCH({
+ *      FOLLY_NON_EXHAUSTIVE_SWITCH({
  *        switch (c) {
  *          case Color::Red:
  *          case Color::LightRed:
@@ -79,22 +79,36 @@
  * Note: The two macros here do not work well before llvm-18, they are known to
  * work for most recent versions of gcc, however.
  */
-#define FOLLY_EXHAUSTIVE_SWITCH(...)                    \
-  FOLLY_PUSH_WARNING                                    \
-  FOLLY_GNU_DISABLE_WARNING("-Wcovered-switch-default") \
-  FOLLY_GNU_ENABLE_ERROR("-Wswitch-default")            \
-  FOLLY_GNU_ENABLE_ERROR("-Wswitch-enum")               \
-  FOLLY_GNU_ENABLE_ERROR("-Wswitch")                    \
-  __VA_ARGS__                                           \
-  FOLLY_POP_WARNING                                     \
+#define FOLLY_EXHAUSTIVE_SWITCH(...)               \
+  FOLLY_PUSH_WARNING                               \
+  FOLLY_GNU_DISABLE_COVERED_SWITCH_DEFAULT_WARNING \
+  FOLLY_GNU_ENABLE_ERROR("-Wswitch-default")       \
+  FOLLY_GNU_ENABLE_ERROR("-Wswitch-enum")          \
+  FOLLY_GNU_ENABLE_ERROR("-Wswitch")               \
+  __VA_ARGS__                                      \
+  FOLLY_POP_WARNING                                \
   static_assert(true, "Add a semicolon after this line for formatting")
 
-#define FOLLY_FLEXIBLE_SWITCH(...)                   \
-  FOLLY_PUSH_WARNING                                 \
-  FOLLY_GNU_DISABLE_WARNING("-Wswitch-enum")         \
-  FOLLY_GNU_ENABLE_ERROR("-Wcovered-switch-default") \
-  FOLLY_GNU_ENABLE_ERROR("-Wswitch-default")         \
-  FOLLY_GNU_ENABLE_ERROR("-Wswitch")                 \
-  __VA_ARGS__                                        \
-  FOLLY_POP_WARNING                                  \
+#define FOLLY_NON_EXHAUSTIVE_SWITCH(...)        \
+  FOLLY_PUSH_WARNING                            \
+  FOLLY_GNU_DISABLE_WARNING("-Wswitch-enum")    \
+  FOLLY_GNU_ENABLE_COVERED_SWITCH_DEFAULT_ERROR \
+  FOLLY_GNU_ENABLE_ERROR("-Wswitch-default")    \
+  FOLLY_GNU_ENABLE_ERROR("-Wswitch")            \
+  __VA_ARGS__                                   \
+  FOLLY_POP_WARNING                             \
   static_assert(true, "Add a semicolon after this line for formatting")
+
+#if defined(__has_warning) && (defined(__GNUC__) || defined(__clang__))
+#if __has_warning("-Wcovered-switch-default")
+#define FOLLY_GNU_DISABLE_COVERED_SWITCH_DEFAULT_WARNING \
+  FOLLY_GNU_DISABLE_WARNING("-Wcovered-switch-default")
+#define FOLLY_GNU_ENABLE_COVERED_SWITCH_DEFAULT_ERROR \
+  FOLLY_GNU_ENABLE_ERROR("-Wcovered-switch-default")
+#endif
+#endif
+
+#ifndef FOLLY_GNU_DISABLE_COVERED_SWITCH_DEFAULT_WARNING
+#define FOLLY_GNU_DISABLE_COVERED_SWITCH_DEFAULT_WARNING
+#define FOLLY_GNU_ENABLE_COVERED_SWITCH_DEFAULT_ERROR
+#endif
diff --git a/folly/folly/lang/ToAscii.h b/folly/folly/lang/ToAscii.h
--- a/folly/folly/lang/ToAscii.h
+++ b/folly/folly/lang/ToAscii.h
@@ -209,11 +209,8 @@
   //  log2 is approx log<2>(v)
   size_t const vlog2 = 64 - static_cast<size_t>(__builtin_clzll(v));
 
-  //  work around msvc warning C4127 (conditional expression is constant)
-  bool false_ = false;
-
   //  handle directly when Base is power-of-two
-  if (false_ || !(Base & (Base - 1))) {
+  if constexpr (!(Base & (Base - 1))) {
     constexpr auto const blog2 = constexpr_log2(Base);
     return vlog2 / blog2 + size_t(vlog2 % blog2 != 0);
   }
diff --git a/folly/folly/lang/UncaughtExceptions.h b/folly/folly/lang/UncaughtExceptions.h
--- a/folly/folly/lang/UncaughtExceptions.h
+++ b/folly/folly/lang/UncaughtExceptions.h
@@ -16,4 +16,4 @@
 
 #pragma once
 
-#include <folly/lang/Exception.h>
+#include <folly/lang/Exception.h> // @shim
diff --git a/folly/folly/lang/bind/AsArgument.h b/folly/folly/lang/bind/AsArgument.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/bind/AsArgument.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Utility.h>
+#include <folly/lang/bind/Bind.h>
+
+///
+/// Implements the bindings semantics of `safe_closure()`.  In this example, it
+/// is responsible for for the "pass by" portion of the logic -- by the time we
+/// invoke `bind_as_argument`, the input ref was already stored as a value
+/// inside the `safe_closure`.
+///
+///   bind::args{
+///       // move `a` in; pass it by const ref
+///       std::move(a),
+///       // copy `b` in; pass it by mutable ref
+///       // deletes `operator() const &`
+///       bind::non_constant{b},
+///       // construct the prvalue inside `fn`
+///       // when passing, decay-copy our stored argument
+///       bind::copy{c()},
+///       // move `d` in; pass it by rvalue ref
+///       // deletes all overloads but `operator() &&`
+///       bind::move{std::move(d)}}
+///
+
+namespace folly::bind::ext { // Details for library authors, not API consumers
+
+template <auto BI, typename StorageRef>
+// Written as a constraint to prevent object slicing
+  requires std::same_as<decltype(BI), bind_info_t>
+constexpr decltype(auto) bind_as_argument(StorageRef&& sr) {
+  if constexpr ( // Default to pass-by-const-ref
+      BI.category == category_t::ref || BI.category == category_t::unset) {
+    if constexpr (
+        BI.constness == constness_t::constant ||
+        BI.constness == constness_t::unset) {
+      // This is like `std::as_const`, but with support for rvalues.
+      // We want rvalue support so that code like this can compile:
+      //   safe_closure(bind::args{a}, [](auto&){})();
+      return static_cast<detail::add_const_inside_ref_t<StorageRef&&>>(sr);
+    } else {
+      static_assert(BI.constness == constness_t::mut);
+      // In principle, this could be relaxed, but we'd need to see a use-case
+      // where binding `bind::mut` by `const` reference is justified.
+      static_assert(
+          !std::is_const_v<std::remove_reference_t<StorageRef>>,
+          "It is likely a bug to use `bind::mut` when the stored argument "
+          "(e.g. the `safe_closure`) is `const`. Hint: If you're invoking a "
+          "lambda capture, recall that copy-captures are `const` by default.");
+      return static_cast<StorageRef&&>(sr);
+    }
+  } else { // Explicit pass-by-value: `bind::move{}` or `bind::copy{}`
+    // There's no explicit modifier for this yet, nor is it clear why you'd
+    // want one in an argument-passing setting.
+    static_assert(BI.category != category_t::value);
+    // Can relax this assert if a use-case dictates sensible semantics.
+    static_assert(
+        BI.constness == constness_t::unset,
+        "It's unclear what `bind::const` / `bind::mut` means when passing "
+        "arguments by-value with `bind::move` or `bind::copy`.");
+    if constexpr (BI.category == category_t::copy) {
+      // Safe to relax, but it's likely a sign of a confused user.
+      static_assert(
+          std::is_reference_v<StorageRef>,
+          "Copying from rvalue storage. Did you mean `bind::move`?");
+      return ::folly::copy(std::forward<StorageRef>(sr));
+    } else {
+      // Fail on `category_t::value`.  It has no explicit modifier yet, nor is
+      // it clear why you'd want one in an argument-passing setting.
+      static_assert(BI.category == category_t::move);
+      // Required so that use-after-move linters consider the storage to be in
+      // a moved-out state.  I.e. "move" is destructive.
+      static_assert(
+          !std::is_reference_v<StorageRef>,
+          "To use `bind::move`, pass the arg storage (e.g. the `safe_closure`) "
+          "by rvalue. Hint: if your `safe_closure` is a lambda capture, recall "
+          "that copy-captures are `const` by default.");
+      return static_cast<StorageRef&&>(sr);
+    }
+  }
+}
+
+} // namespace folly::bind::ext
diff --git a/folly/folly/lang/bind/Bind.h b/folly/folly/lang/bind/Bind.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/bind/Bind.h
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Utility.h>
+#include <folly/detail/tuple.h>
+
+FOLLY_PUSH_WARNING
+FOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS
+
+///
+/// READ ME: The docs for this library are in `Bind.md`.
+///
+
+namespace folly::bind {
+
+template <typename, typename... As>
+constexpr auto in_place(As&&...);
+template <typename... As>
+constexpr auto in_place_with(auto, As&&...);
+
+// The primitive for representing lists of bound args. Unary forms:
+//  - `bind::args<V>`: if `V` is already `like_args`, wraps that
+//    binding, unmodified.
+//  - `bind::args<V&>` or `bind::args<V&&>`: binds a reference preserving
+//    the value category of the input.
+// Plural `bind::args<Ts...>` is a concatenation of many `like_args`,
+// equivalent to `merge_update_args` with `std::identity`.
+template <typename...>
+class args;
+
+namespace detail::lite_tuple {
+using namespace folly::detail::lite_tuple;
+}
+
+namespace ext { // For extension authors -- public details
+
+// Any object modeling a list of bound args with modifiers must publicly
+// derive from `like_args`, and must also implement:
+//  public:
+//   using binding_list_t = ...;
+//   constexpr auto unsafe_tuple_to_bind() && noexcept [[clang::lifetimebound]]
+//   constexpr YOUR_CLASS(unsafe_move_args, tuple_to_bind_t t);
+class like_args : NonCopyableNonMovable {};
+
+struct unsafe_move_args {
+  template <std::derived_from<ext::like_args> BoundArgs>
+  static constexpr auto from(BoundArgs&& bargs) {
+    return BoundArgs{
+        unsafe_move_args{},
+        static_cast<BoundArgs&&>(bargs).unsafe_tuple_to_bind()};
+  }
+};
+
+// A binding consists of:
+//   - An "input type", most often a forwarding reference stored in the
+//     `typename` of `binding_t`.  Or, if that is a value type, then we're
+//     doing in-place construction, and the input is a "maker" object that's
+//     implicitly convertible to that value type.
+//   - "Flags" stored in `bind_info_t` enums below.  Users set them via
+//     modifiers like `constant{}` or `const_ref{}`.  Library authors may
+//     derive from `bind_info_t` to add custom flags, and then customize the
+//     policies in `ToStorage.h` or `AsArgument.h` to handle them.
+//
+// Each flag must default to `unset` so that each policy can dictate behavior.
+// For example, standard storage policy is: non-`const` output for
+// pass-by-value, `const` for pass-by-ref.  But, with this flag + policy
+// design, an API can elect an alternate "always `const`" policy while still
+// using the standard modifier vocabulary from this file.
+enum class category_t {
+  // The policy decides.  The standard storage policy uses `value`, while the
+  // argument-binding one uses `ref`.
+  unset = 0,
+  ref, // Follows reference category of the input, i.e. `InputT&&`.
+  value, // Copies or moves, depending on input ref type.
+  // `bind_to_storage_policy`: Like `value`, but fails on rvalue input.
+  // `bind_as_argument`: Callee gets `folly::copy()` as an argument.
+  copy,
+  // `bind_to_storage_policy`: Like `value`, but fails on lvalue input.
+  // `bind_as_argument`: Callee gets the rvalue ref as an argument.
+  move
+};
+enum class constness_t {
+  // The policy decides.  The standard storage & argument-binding policies use
+  // `const` for refs, `mut` for values.
+  unset = 0,
+  // For reference types, these both affect the underlying value type:
+  constant, // Make the input `const` if it's not already.
+  mut // Will NOT remove `const` from an input type
+};
+struct bind_info_t {
+  category_t category;
+  constness_t constness;
+
+  explicit bind_info_t() = default;
+  constexpr explicit bind_info_t(category_t ct, constness_t cn)
+      : category(ct), constness(cn) {}
+};
+
+// Metadata for a bound arg, capturing:
+//   - `BindingType`: the input binding type -- a reference, unless this is a
+//     `bind::in_place*` -- see `is_binding_t_type_in_place`.
+//   - A `bind_info_t`-derived object capturing the effects of any binding
+//     modifiers (`constant`, `const_ref`, etc).
+//
+// This is used by policies like `bind_to_storage_policy` and
+// `bind_as_argument()`.  The values returned by `unsafe_tuple_to_bind()`
+// should be convertible to the resulting `::storage_type`.
+template <std::derived_from<bind_info_t> auto, typename BindingType>
+struct binding_t {};
+
+// CAUTION: Applicable ONLY to `BT` from `binding_t<BI, BT>`.
+// Contrast to `detail::is_in_place_maker`.
+template <typename BT>
+concept is_binding_t_type_in_place = !std::is_reference_v<BT>;
+
+// Implementation detail for all the `bind::args` modifiers (`constant`,
+// `const_ref`, etc).  Concatenates multiple `Ts`, each quacking like
+// `like_args`, to produce a single `like_args` list.  Applies
+// `BindInfoFn` to each `bind_info_t` in the inputs' `binding_list_t`s.
+template <typename BindInfoFn, typename... Ts>
+class merge_update_args : public like_args {
+ private:
+  using tuple_to_bind_t = decltype(detail::lite_tuple::tuple_cat(
+      std::declval<args<Ts>>().unsafe_tuple_to_bind()...));
+  tuple_to_bind_t tup_;
+
+ protected:
+  ~merge_update_args() = default; // Only used as a base class.
+
+ public:
+  // Concatenate `Ts::binding_list_t...` after mapping their `bind_info_t`s
+  // through `BindInfoFn`.
+  using binding_list_t =
+      decltype([]<auto... BIs, typename... BTs>(tag_t<binding_t<BIs, BTs>...>) {
+        return tag<binding_t<BindInfoFn{}(BIs), BTs>...>;
+      }(type_list_concat_t<tag_t, typename args<Ts>::binding_list_t...>{}));
+
+  explicit constexpr merge_update_args(args<Ts>... ts) noexcept
+      : tup_(
+            detail::lite_tuple::tuple_cat(
+                std::move(ts).unsafe_tuple_to_bind()...)) {}
+
+  // `lifetimebound` documented in `in_place_args_crtp_base`
+  constexpr auto unsafe_tuple_to_bind() && noexcept [[clang::lifetimebound]] {
+    return std::move(tup_);
+  }
+
+  constexpr merge_update_args(unsafe_move_args, tuple_to_bind_t t)
+      : tup_(std::move(t)) {}
+};
+
+// This is cosmetic -- the point is for the signatures of `bind::args<>` and
+// similar templates to show rvalue reference bindings with `&&`.
+template <typename T>
+using deduce_args_t =
+    std::conditional_t<std::derived_from<T, like_args>, T, T&&>;
+
+} // namespace ext
+
+// Binds an input reference (lvalue or rvalue)
+template <typename T>
+  requires(!std::derived_from<T, ext::like_args>)
+class args<T> : public ext::like_args {
+  static_assert(
+      std::is_reference_v<T>,
+      "Check that your deduction guide has `deduce_args_t`");
+
+ private:
+  T& ref_;
+
+ public:
+  using binding_list_t = tag_t<ext::binding_t<ext::bind_info_t{}, T&&>>;
+
+  constexpr /*implicit*/ args(T&& t) noexcept : ref_(t) {}
+
+  // `lifetimebound` documented in `in_place_args_crtp_base`
+  constexpr auto unsafe_tuple_to_bind() && noexcept [[clang::lifetimebound]] {
+    return detail::lite_tuple::tuple<T&&>{static_cast<T&&>(ref_)};
+  }
+
+  constexpr args(ext::unsafe_move_args, detail::lite_tuple::tuple<T&&> t)
+      : ref_(detail::lite_tuple::get<0>(t)) {}
+};
+
+// This specialization is instantiated when a binding modifier (usually derived
+// from `merge_update_args`), or a `bind::in_place*`, gets passed to an
+// object taking `bind::args<Ts>...`.
+//
+// It wraps another `like_args`, by value.  This preserves the
+// interface (`binding_list_t`, `unsafe_tuple_to_bind`, etc) of the
+// underlying class.  It only exists to allow constructors of bound args
+// aggregates to take their inputs by-value as `bind::args<Ts>...`.
+//
+// This "always wrap" design is necessary because `like_args` are both
+// immovable AND unsafe for end-users to pass by-reference.  They contain
+// references themselves, so they should only exist as prvalues for the
+// duration of one statement (so that C++ reference lifetime extension
+// guarantees safety).  So, we pass them by-value, and use the
+// `unsafe_move_args` ctor to move the innards from the prvalue argument
+// into the wrapper.
+//
+// NB It is not typical for `T` to be another `bind::args`, but it's also
+// perfectly fine, compositionally speaking, so it is allowed.
+template <std::derived_from<ext::like_args> T>
+class args<T> : public T {
+ public:
+  constexpr /*implicit*/ args(T t)
+      : T(ext::unsafe_move_args{}, std::move(t).unsafe_tuple_to_bind()) {}
+
+  constexpr args(ext::unsafe_move_args, auto tup)
+      : T(ext::unsafe_move_args{}, std::move(tup)) {}
+};
+
+template <typename... Ts>
+  requires(sizeof...(Ts) != 1)
+class args<Ts...> : public ext::merge_update_args<std::identity, Ts...> {
+  using ext::merge_update_args<std::identity, Ts...>::merge_update_args;
+};
+template <typename... Ts>
+args(Ts&&...) -> args<ext::deduce_args_t<Ts>...>;
+
+namespace detail { // Private details
+
+template <typename T, typename Maker>
+class in_place_args_crtp_base : public ::folly::bind::ext::like_args {
+ private:
+  static_assert(!std::is_reference_v<T>);
+  static_assert( // This would be an unexpected usage.
+      !std::derived_from<T, ::folly::bind::ext::like_args>);
+
+ protected:
+  using maker_type = Maker;
+  maker_type maker_;
+
+  constexpr in_place_args_crtp_base(
+      ext::unsafe_move_args, detail::lite_tuple::tuple<maker_type> t)
+      : maker_(std::move(detail::lite_tuple::get<0>(t))) {}
+
+  constexpr explicit in_place_args_crtp_base(maker_type maker)
+      : maker_(std::move(maker)) {}
+
+ public:
+  using binding_list_t = tag_t<ext::binding_t<ext::bind_info_t{}, T>>;
+
+  // Technically, the `lifetimebound` below is too conservative, because we
+  // hand ownership of the refs in `maker_` to the caller.  However, since
+  // `like_args` must never exist for more than one statement, this
+  // should not be a problem in practical usage.
+  //
+  // The annotation's benefit is that it detects real implementation bugs.
+  // See `all_tests_run_at_build_time` for a manually compilable example.
+  // In short, if you removed this `lifetimebound`, the compiler could no
+  // longer catch this dangling ref --
+  //   // BAD: Contained prvalue `&made` becomes invalid at the `;`
+  //   auto fooMaker = bind::in_place<Foo>(&made, n).unsafe_tuple_to_bind();
+  //   Foo foo = std::move(fooMaker);
+
+  // To allow in-place construction within a `tuple<..., T, ...>`, this
+  // returns a `Maker` that's implicitly convertible to `T`.
+  constexpr auto unsafe_tuple_to_bind() && noexcept [[clang::lifetimebound]] {
+    return detail::lite_tuple::tuple{std::move(maker_)};
+  }
+};
+
+// Both "maker" classes are move-only to help keep them single-use.
+template <typename T, typename... Args>
+class in_place_args_maker : private MoveOnly {
+ private:
+  // `&&` allows binding rvalues. Safe, since a binding lives for 1 statement.
+  detail::lite_tuple::tuple<Args&&...> arg_tup_;
+
+ protected:
+  template <typename, typename...>
+  friend class in_place_args;
+
+  constexpr /*implicit*/ in_place_args_maker(
+      Args&&... as [[clang::lifetimebound]]) noexcept
+      : arg_tup_{static_cast<Args&&>(as)...} {}
+
+ public:
+  // This implicit conversion allows direct construction inside of `tuple` e.g.
+  constexpr /*implicit*/ operator T() && {
+    return lite_tuple::apply(
+        [](auto&&... as) { return T{static_cast<Args&&>(as)...}; },
+        std::move(arg_tup_));
+  }
+  // Power users may want to rewrite the args of an in-place binding.
+  constexpr auto&& release_arg_tuple() && noexcept [[clang::lifetimebound]] {
+    return std::move(arg_tup_);
+  }
+};
+
+// NB: `Args` are deduced by `bind::in_place` as forwarding references
+template <typename T, typename... Args>
+class in_place_args
+    : public in_place_args_crtp_base<T, in_place_args_maker<T, Args...>> {
+ protected:
+  template <typename, typename... As>
+  friend constexpr auto ::folly::bind::in_place(As&&...);
+
+  using base = in_place_args_crtp_base<T, in_place_args_maker<T, Args...>>;
+  constexpr explicit in_place_args(Args&&... args [[clang::lifetimebound]])
+      : base{{static_cast<Args&&>(args)...}} {}
+
+ public:
+  constexpr in_place_args(ext::unsafe_move_args u, auto t)
+      : base{u, std::move(t)} {}
+};
+
+template <typename T, typename Fn, typename... Args>
+class in_place_fn_maker : private MoveOnly {
+ private:
+  Fn fn_; // May contain refs; ~safe since a binding lives for 1 statement.
+  detail::lite_tuple::tuple<Args&&...> arg_tup_;
+
+ protected:
+  template <typename, typename, typename...>
+  friend class in_place_fn_args;
+
+  constexpr /*implicit*/ in_place_fn_maker(
+      Fn fn, Args&&... as [[clang::lifetimebound]])
+      : fn_(std::move(fn)), arg_tup_{static_cast<Args&&>(as)...} {}
+
+ public:
+  // This implicit conversion allows direct construction inside of `tuple` e.g.
+  constexpr /*implicit*/ operator T() && {
+    return lite_tuple::apply(fn_, arg_tup_);
+  }
+};
+
+// NB: `Args` are deduced by `bind::in_place` as forwarding references
+template <typename T, typename Fn, typename... Args>
+class in_place_fn_args
+    : public in_place_args_crtp_base<T, in_place_fn_maker<T, Fn, Args...>> {
+ protected:
+  template <typename... As>
+  friend constexpr auto ::folly::bind::in_place_with(auto, As&&...);
+
+  using base = in_place_args_crtp_base<T, in_place_fn_maker<T, Fn, Args...>>;
+  constexpr explicit in_place_fn_args(
+      Fn fn, Args&&... args [[clang::lifetimebound]])
+      : base{{std::move(fn), static_cast<Args&&>(args)...}} {}
+
+ public:
+  constexpr in_place_fn_args(ext::unsafe_move_args u, auto t)
+      : base{u, std::move(t)} {}
+};
+
+// If using this with `BT` from `binding_t<BI, BT>`, do not `remove_reference`
+// first, since such `BT` is always a value for in-place bindings.
+template <typename T>
+concept is_in_place_maker = instantiated_from<T, in_place_fn_maker> ||
+    instantiated_from<T, in_place_args_maker>;
+
+using constant_bind_info = decltype([](auto bi) {
+  bi.constness = ext::constness_t::constant;
+  return bi;
+});
+
+using mut_bind_info = decltype([](auto bi) {
+  bi.constness = ext::constness_t::mut;
+  return bi;
+});
+
+using const_ref_bind_info = decltype([](auto bi) {
+  bi.category = ext::category_t::ref;
+  bi.constness = ext::constness_t::constant;
+  return bi;
+});
+
+using mut_ref_bind_info = decltype([](auto bi) {
+  bi.category = ext::category_t::ref;
+  bi.constness = ext::constness_t::mut;
+  return bi;
+});
+
+using move_bind_info = decltype([](auto bi) {
+  bi.category = ext::category_t::move;
+  return bi;
+});
+
+using copy_bind_info = decltype([](auto bi) {
+  bi.category = ext::category_t::copy;
+  return bi;
+});
+
+} // namespace detail
+
+// `bind::in_place` and `bind::in_place_with` construct non-movable,
+// non-copyable types in their final location.
+//
+// CAREFUL: As with other `bind::args{}`, the returned object stores references
+// to `args` to avoid unnecessary move-copies.  A power user may wish to write
+// a function that returns a binding, which OWNS some values.  For example, you
+// may need to avoid a stack-use-after-free such as this one:
+//
+//   auto makeFoo(int n) { return bind::in_place<Foo>(n); } // BAD: `n` is dead
+//
+// To avoid the bug, either take `n` by-reference (often preferred), or store
+// your values inside a `bind::in_place_with` callable:
+//
+//   auto makeFoo(int n) {
+//     return bind::in_place_with([n]() { return Foo{n}; });
+//   }
+template <typename T, typename... Args>
+constexpr auto in_place(Args&&... args [[clang::lifetimebound]]) {
+  return detail::in_place_args<T, Args...>{static_cast<Args&&>(args)...};
+}
+// This is second-choice compared to `bind::in_place` because:
+//   - Dangling references may be hidden inside `make_fn` captures --
+//     `clang` offers no `lifetimebound` analysis for these (yet?).
+//   - The type signature of the `in_place_args` includes a lambda.
+// CAREFUL: While `make_fn` is taken by-value, `args` are stored as references,
+// as in `bind::in_place`.
+template <typename... Args>
+constexpr auto in_place_with(
+    auto make_fn, Args&&... args [[clang::lifetimebound]]) {
+  return detail::in_place_fn_args<
+      decltype(make_fn(FOLLY_DECLVAL(Args&&)...)),
+      decltype(make_fn),
+      Args...>{std::move(make_fn), static_cast<Args&&>(args)...};
+}
+
+// The below "binding modifiers" all return an immovable bound args list,
+// meant to be passed only by-value, as a prvalue.  They can all take unary,
+// or plural `like_args` as inputs.  Their only difference from
+// `bind::args{values...}` is that these modifiers override some aspect of
+// `bind_info_t`s on on all the bindings they contain.
+//
+// Unlike standard C++, the "constant" and "ref" modifiers commute, avoiding
+// that common footgun.
+//
+// You can think of these analogously to value category specifiers for
+// member variables of a struct -- e.g.
+//
+//   (1) `mut_ref{}` asks to store a non-const reference, of
+//        the same reference category as the input.  Or, see `const_ref{}`.
+//   (2) `constant{}` + `bind_to_storage_policy` stores the input as a `const`
+//        value.  This allows you to move a non-const reference into a `const`
+//        storage location.
+//   (3) `constant{}` + `bind_as_argument()` passes a stored reference
+//       via `std::as_const`.
+//
+// Specifiers can be overridden, e.g. you could (but should not!) express
+// `mut_ref{}` as `mut{const_ref{}}`.
+//
+// There's currently no user-facing `by_ref{}`, which would leave the
+// `constness` of the binding to be defaulted by each policy.  If a use-case
+// arises, the test already contains its trivial implementation.
+
+template <typename... Ts>
+struct constant : ext::merge_update_args<detail::constant_bind_info, Ts...> {
+  using ext::merge_update_args<detail::constant_bind_info, Ts...>::
+      merge_update_args;
+};
+template <typename... Ts>
+constant(Ts&&...) -> constant<ext::deduce_args_t<Ts>...>;
+
+template <typename... Ts>
+struct mut : ext::merge_update_args<detail::mut_bind_info, Ts...> {
+  using ext::merge_update_args<detail::mut_bind_info, Ts...>::merge_update_args;
+};
+template <typename... Ts>
+mut(Ts&&...) -> mut<ext::deduce_args_t<Ts>...>;
+
+template <typename... Ts>
+struct const_ref : ext::merge_update_args<detail::const_ref_bind_info, Ts...> {
+  using ext::merge_update_args<detail::const_ref_bind_info, Ts...>::
+      merge_update_args;
+};
+template <typename... Ts>
+const_ref(Ts&&...) -> const_ref<ext::deduce_args_t<Ts>...>;
+
+template <typename... Ts>
+struct mut_ref : ext::merge_update_args<detail::mut_ref_bind_info, Ts...> {
+  using ext::merge_update_args<detail::mut_ref_bind_info, Ts...>::
+      merge_update_args;
+};
+template <typename... Ts>
+mut_ref(Ts&&...) -> mut_ref<ext::deduce_args_t<Ts>...>;
+
+// `bind::copy` & `bind::move` -- see `category_t` for the behaviors in brief
+//
+// For APIs that use `bind::` modifiers to pass arguments (`safe_closure`),
+// these pick between passing an rvalue ref into the callee, or passing a
+// decay-copied prvalue (using `folly::copy`).
+//
+// For APIs that use `bind::` modifiers to store the inputs (`async_closure`
+// uses `bind_to_storage_policy`, e.g.), these verbs aren't very useful --
+// they're equivalent to the default store-by-value, plus a static assert.
+
+template <typename... Ts>
+struct move : ext::merge_update_args<detail::move_bind_info, Ts...> {
+  using ext::merge_update_args<detail::move_bind_info, Ts...>::
+      merge_update_args;
+};
+template <typename... Ts>
+move(Ts&&...) -> move<ext::deduce_args_t<Ts>...>;
+
+template <typename... Ts>
+struct copy : ext::merge_update_args<detail::copy_bind_info, Ts...> {
+  using ext::merge_update_args<detail::copy_bind_info, Ts...>::
+      merge_update_args;
+};
+template <typename... Ts>
+copy(Ts&&...) -> copy<ext::deduce_args_t<Ts>...>;
+
+namespace ext::detail { // Common detail for `AsArgument.h` and `ToStorage.h`
+// Similar to `std::as_const`, but only computes the type, and works on
+// rvalue references.
+//
+// Future: This **might** compile faster with a family of explicit
+// specializations, see e.g. `folly::like_t`'s implementation.
+template <typename T>
+using add_const_inside_ref_t = std::conditional_t<
+    std::is_rvalue_reference_v<T>,
+    typename std::add_const<std::remove_reference_t<T>>::type&&,
+    std::conditional_t<
+        std::is_lvalue_reference_v<T>,
+        typename std::add_const<std::remove_reference_t<T>>::type&,
+        typename std::add_const<std::remove_reference_t<T>>::type>>;
+} // namespace ext::detail
+
+} // namespace folly::bind
+
+FOLLY_POP_WARNING
diff --git a/folly/folly/lang/bind/Named.h b/folly/folly/lang/bind/Named.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/bind/Named.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <tuple>
+#include <type_traits>
+
+#include <folly/Traits.h>
+#include <folly/lang/bind/Bind.h>
+
+///
+/// Extends `Bind.h` (read that first) with keyword-argument syntax:
+///   bind::args{
+///      self_id = bind::in_place<MyClass>(), // tag akin to Python's `self`
+///      "x"_id = 5,
+///      "y"_id = mut_ref{y},
+///   }
+///
+/// If the API takes a pack of named bindings, the above syntax is all that a
+/// callers needs to know.  Some API mays require you to wrap the pack with a
+/// custom container like `named_values{...}` (not yet built).
+///
+
+namespace folly::bind {
+
+namespace ext { // For extension authors -- public details
+
+template <auto Tag, std::derived_from<folly::bind::ext::bind_info_t> Base>
+struct named_bind_info_t : Base {
+  constexpr const Base& as_base() const { return *this; }
+  using Base::Base;
+  constexpr explicit named_bind_info_t(
+      // Use a constraint to prevent object slicing.
+      std::same_as<Base> auto b)
+      : Base(std::move(b)) {}
+};
+
+namespace detail {
+template <auto Tag>
+struct named_bind_info {
+  template <typename BI>
+    requires(
+        // Use a constraint to prevent object slicing.
+        std::derived_from<BI, bind_info_t>
+        // XXX Future: && a clause to prevent chaining assignments.
+        )
+  constexpr named_bind_info_t<Tag, BI> operator()(BI bi) const {
+    return named_bind_info_t<Tag, BI>{std::move(bi)};
+  }
+};
+} // namespace detail
+
+template <literal_string Str, typename T>
+struct id_arg : ext::merge_update_args<detail::named_bind_info<Str>, T> {
+  using ext::merge_update_args<detail::named_bind_info<Str>, T>::
+      merge_update_args;
+};
+
+struct self_id_t {};
+
+template <typename T>
+struct self_id_arg
+    : ext::merge_update_args<detail::named_bind_info<self_id_t{}>, T> {
+  using ext::merge_update_args<detail::named_bind_info<self_id_t{}>, T>::
+      merge_update_args;
+};
+
+template <auto Tag> // `literal_string<"x">` or `self_id_t{}`
+class identifier {
+ private:
+  template <typename T, literal_string Str>
+  static constexpr id_arg<Str, T> make_with_tag(vtag_t<Str>, auto&& ba) {
+    return id_arg<Str, T>{unsafe_move_args::from(std::move(ba))};
+  }
+  template <typename T>
+  static constexpr self_id_arg<T> make_with_tag(
+      vtag_t<self_id_t{}>, auto&& ba) {
+    return self_id_arg<T>{unsafe_move_args::from(std::move(ba))};
+  }
+
+ public:
+  // "x"_id = some_binding_modifier{var}
+  constexpr auto operator=(std::derived_from<ext::like_args> auto ba) const {
+    []<typename... BTs>(tag_t<BTs...>) {
+      static_assert(
+          sizeof...(BTs) == 1,
+          "Cannot give the same name to multiple bound args.");
+    }(typename decltype(ba)::binding_list_t{});
+    return make_with_tag<decltype(ba)>(vtag<Tag>, std::move(ba));
+  }
+  // "x"_id = var
+  template <typename T>
+    requires(!std::derived_from<T, ext::like_args>)
+  constexpr auto operator=(T&& t) const {
+    return make_with_tag<T&&>(vtag<Tag>, args{static_cast<T&&>(t)});
+  }
+
+  static inline constexpr auto folly_bindings_identifier_tag = Tag;
+};
+
+} // namespace ext
+
+// These types represents "identifier tag -> storage type" signatures for
+// bindings. Library authors: search for `signature_type`.
+//
+// NB: `id_type` is distinct from `self_id_type` for cleaner error messages.
+// Logically, both of these are `tag_type` (or similar), and the
+// implementation could later be generalized, but for now, this is ok.
+template <typename UnderlyingSig>
+struct self_id_type {
+  using identifier_type = ext::identifier<ext::self_id_t{}>;
+  using storage_type = UnderlyingSig::storage_type;
+};
+template <literal_string Str, typename UnderlyingSig>
+struct id_type {
+  using identifier_type = ext::identifier<Str>;
+  using storage_type = UnderlyingSig::storage_type;
+};
+
+template <literal_string Str>
+inline constexpr ext::identifier<Str> id{};
+
+// For brevity, `bind::id<"x">` can be made via the literal `"x"_id`.
+inline namespace literals {
+inline namespace string_literals {
+template <literal_string Str>
+consteval auto operator""_id() noexcept {
+  return id<Str>;
+}
+} // namespace string_literals
+} // namespace literals
+
+// A special `identifier` tag, which works just like `"x"_id`. It's meant as
+// syntax sugar for "self-reference" scenarios, such as:
+//   - Tagging for the implicit scope/object parameter in `spawn_self_closure`.
+//   - Tagging `this`-pointer-like objects when calling class member functions
+//     with a kwarg calling convention.
+// While it somewhat resembles the role of the `this` pointer, the name
+// `this_id` is ambiguous (which ID?), and doesn't sound well in all use-cases.
+inline constexpr ext::identifier<ext::self_id_t{}> self_id;
+
+} // namespace folly::bind
diff --git a/folly/folly/lang/bind/NamedToStorage.h b/folly/folly/lang/bind/NamedToStorage.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/bind/NamedToStorage.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/lang/bind/Named.h>
+#include <folly/lang/bind/ToStorage.h>
+
+///
+/// Adds `Named.h` tags to the `signature_type` from `ToStorage.h`.
+///
+/// Future: It would be better to refactor the `*_bind_info_t` types NOT to be
+/// related by public inheritance -- they don't satisfy Liskov's substitution
+/// principle.  Instead, have them quack-a-like while sharing implementation
+/// privately (or via composition).  That would mitigate the ever-present
+/// slicing risk, and reduce coupling.  If the # of types proliferates besides
+/// 2 or 3, this may be relevant:
+/// https://gist.github.com/snarkmaster/cfa209a4d05104a78769ca8062f382a0
+///
+
+namespace folly::bind::ext { // Details for library authors, not API consumers
+
+struct no_tag_t {};
+
+template <typename>
+inline constexpr no_tag_t named_bind_info_tag_v{};
+
+template <auto Tag, typename Base>
+inline constexpr auto named_bind_info_tag_v<named_bind_info_t<Tag, Base>> = Tag;
+
+// Named bindings follow the binding policy of the underlying base
+template <auto NBI, typename BindingType>
+// Use a constraint to avoid object slicing
+  requires(!std::is_same_v<
+           const no_tag_t,
+           decltype(named_bind_info_tag_v<decltype(NBI)>)>)
+class bind_to_storage_policy<ext::binding_t<NBI, BindingType>> {
+ private:
+  using underlying =
+      bind_to_storage_policy<ext::binding_t<NBI.as_base(), BindingType>>;
+
+  template <typename Base>
+  static constexpr auto signature_type_for(
+      const named_bind_info_t<ext::self_id_t{}, Base>&)
+      -> self_id_type<typename underlying::signature_type>;
+
+  template <literal_string Str, typename Base>
+  static constexpr auto signature_type_for(const named_bind_info_t<Str, Base>&)
+      -> id_type<Str, typename underlying::signature_type>;
+
+ public:
+  using storage_type = typename underlying::storage_type;
+  using signature_type = decltype(signature_type_for(NBI));
+};
+
+} // namespace folly::bind::ext
diff --git a/folly/folly/lang/bind/ToStorage.h b/folly/folly/lang/bind/ToStorage.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/bind/ToStorage.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Utility.h>
+#include <folly/lang/bind/Bind.h>
+
+///
+/// Implements standard storage semantics for `folly::bind`.  This is used
+/// (with some additional constraints & tweaks) by `async_closure` captures.
+/// It can also be used for other single-use data structurs -- this makes it
+/// easy to simultaneously define the types for, and construct, tuples, named
+/// tuples, structs, etc. Some examples of this are given in `Bind.md`.
+///
+
+namespace folly::bind::ext { // Details for library authors, not API consumers
+
+template <typename, typename = void>
+class bind_to_storage_policy;
+
+// This is a separate class so that libraries can customize the binding
+// policy enacted by `bind_to_storage_policy` by detecting their custom fields
+// in `BI`.  See `named/Binding.h` for an example.
+//
+// IMPORTANT:
+//  - Only specialize `bind_to_storage_policy` for the specific derived classes
+//    of `bind_info_t` that you own -- DO NOT overmatch!
+//
+//  - DO delegate handling of the base `bind_info_t` to this specialization, by
+//    slicing your input BI.  See `NamedToStorage.h` for an example & a
+//    relevant future refactor.
+//
+//    Any deviations from the standard policy are likely to confuse users &
+//    readers of your library, and are probably not worth it.  If you REALLY
+//    need to deviate (ex: default bindings to `const`), make the name of
+//    your API reflect this (ex: `fooDefaultConst()`).
+template <auto BI, typename BindingType>
+// Formulated as a constraint to prevent object slicing
+  requires std::same_as<decltype(BI), bind_info_t>
+class bind_to_storage_policy<binding_t<BI, BindingType>> {
+  // If `BindingType` is incomplete, it means we likely can't see the relevant
+  // `bind_to_storage_policy` specializations, either.
+  static_assert(require_sizeof<BindingType> >= 0);
+  static_assert(
+      !is_binding_t_type_in_place<BindingType> ||
+          BI.category != category_t::ref,
+      "`const_ref` / `mut_ref` is incompatible with `bind::in_place*`");
+
+ protected:
+  constexpr static auto project_type() {
+    if constexpr (BI.category == category_t::ref) {
+      // By-reference: `const` by default
+      if constexpr (BI.constness == constness_t::mut) {
+        return std::type_identity<BindingType&&>{}; // Leave existing `const`
+      } else {
+        return std::type_identity<
+            detail::add_const_inside_ref_t<BindingType>&&>{};
+      }
+    } else {
+      if constexpr (BI.category == category_t::copy) {
+        static_assert(!std::is_rvalue_reference_v<BindingType>);
+      } else if constexpr (BI.category == category_t::move) {
+        static_assert(std::is_rvalue_reference_v<BindingType>);
+      } else {
+        static_assert(
+            (BI.category == category_t::value) ||
+            (BI.category == category_t::unset));
+      }
+      // By-value: non-`const` by default
+      using UnrefT = std::remove_reference_t<BindingType>;
+      if constexpr (BI.constness == constness_t::constant) {
+        return std::type_identity<const UnrefT>{};
+      } else {
+        return std::type_identity<UnrefT>{};
+      }
+    }
+  }
+
+ public:
+  using storage_type = typename decltype(project_type())::type;
+  // Why is this here?  With named bindings, we want the signature of a
+  // binding list to show the identifier's name to the user.
+  using signature_type = storage_type;
+};
+
+} // namespace folly::bind::ext
diff --git a/folly/folly/lang/cstring_view.h b/folly/folly/lang/cstring_view.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/lang/cstring_view.h
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cassert>
+#include <cstring>
+#include <functional>
+#include <iosfwd>
+#include <string_view>
+
+#include <fmt/format.h>
+
+#include <folly/Traits.h>
+#include <folly/Utility.h>
+
+namespace folly {
+
+/// cstring_view
+///
+/// A string view type that privately inherits from std::string_view but
+/// guarantees that the underlying buffer is null-terminated. This allows safe
+/// use of .c_str() while maintaining all the benefits of string_view.
+///
+/// The type provides adjusted constructors and assignment operators to ensure
+/// the null-termination guarantee is maintained.
+///
+/// mimic: std::cstring_view, p3655r3
+///
+/// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3655r3.html
+template <typename Char, typename Traits = std::char_traits<Char>>
+class basic_cstring_view {
+  using self = basic_cstring_view;
+  using view_type = std::basic_string_view<Char, Traits>;
+
+  template <typename T>
+  using detect_c_str = decltype(FOLLY_DECLVAL(T const&).c_str());
+
+  static constexpr Char const* check_string(
+      Char const* str, [[maybe_unused]] size_t len) noexcept {
+    assert((!str && !len) || Traits::length(str) == len);
+    return str;
+  }
+  template <typename String>
+  static constexpr String const& check_string(String const& str) noexcept {
+    check_string(str.c_str(), str.size());
+    return str;
+  }
+
+ public:
+  using const_iterator = typename view_type::const_iterator;
+  using const_pointer = typename view_type::const_pointer;
+  using const_reference = typename view_type::const_reference;
+  using const_reverse_iterator = typename view_type::const_reverse_iterator;
+  using difference_type = typename view_type::difference_type;
+  using iterator = typename view_type::iterator;
+  using pointer = typename view_type::pointer;
+  using reference = typename view_type::reference;
+  using reverse_iterator = typename view_type::reverse_iterator;
+  using size_type = typename view_type::size_type;
+  using traits_type = typename view_type::traits_type;
+  using value_type = typename view_type::value_type;
+
+  static constexpr size_type npos = view_type::npos;
+
+ private:
+  view_type view_;
+
+ public:
+  constexpr basic_cstring_view() noexcept = default;
+
+  constexpr basic_cstring_view(std::nullptr_t) = delete;
+
+  /* implicit */ constexpr basic_cstring_view(Char const* str) noexcept
+      : view_(!str ? view_type{} : view_type{str}) {}
+
+  /// Diverges from p3655r3 in that we allow construction from a null pointer.
+  /// Members data() and c_str() will return nullptr. Mimics std::string_view
+  /// behavior v.s. std::string behavior.
+  ///
+  /// The paper tries to make std::cstring_view work like a string and not like
+  /// a view (a view is a structure with a non-owning pointer and a length),
+  /// creating a discrepancy between std::string_view and std::cstring_view.
+  constexpr basic_cstring_view(Char const* str, std::size_t len) noexcept
+      : view_(check_string(str, len), len) {}
+
+  template <typename String, typename = detect_c_str<String>>
+  /* implicit */ constexpr basic_cstring_view(String const& str) noexcept
+      : view_(check_string(str)) {}
+
+  constexpr basic_cstring_view(basic_cstring_view const&) noexcept = default;
+
+  constexpr basic_cstring_view& operator=(basic_cstring_view const&) noexcept =
+      default;
+
+  constexpr basic_cstring_view& operator=(Char const* str) noexcept {
+    view_ = str;
+    return *this;
+  }
+
+  template <typename String, typename..., typename = detect_c_str<String>>
+  constexpr basic_cstring_view& operator=(String const& str) noexcept {
+    view_ = check_string(str);
+    return *this;
+  }
+
+  // Accessor methods
+  constexpr const_reference operator[](size_type pos) const {
+    return view_[pos];
+  }
+  constexpr const_reference at(size_type pos) const { return view_.at(pos); }
+  constexpr const_reference front() const { return view_.front(); }
+  constexpr const_reference back() const { return view_.back(); }
+  constexpr const_pointer data() const noexcept { return view_.data(); }
+
+  // Iterator methods
+  constexpr const_iterator begin() const noexcept { return view_.begin(); }
+  constexpr const_iterator end() const noexcept { return view_.end(); }
+  constexpr const_iterator cbegin() const noexcept { return view_.cbegin(); }
+  constexpr const_iterator cend() const noexcept { return view_.cend(); }
+  constexpr const_reverse_iterator rbegin() const noexcept {
+    return view_.rbegin();
+  }
+  constexpr const_reverse_iterator rend() const noexcept {
+    return view_.rend();
+  }
+  constexpr const_reverse_iterator crbegin() const noexcept {
+    return view_.crbegin();
+  }
+  constexpr const_reverse_iterator crend() const noexcept {
+    return view_.crend();
+  }
+
+  // Capacity methods
+  constexpr size_type size() const noexcept { return view_.size(); }
+  constexpr size_type length() const noexcept { return view_.length(); }
+  constexpr size_type max_size() const noexcept { return view_.max_size(); }
+  constexpr bool empty() const noexcept { return view_.empty(); }
+
+  // Modifiers
+  constexpr void remove_prefix(size_type n) { view_.remove_prefix(n); }
+
+  // String operations
+  constexpr size_type find(view_type sv, size_type pos = 0) const noexcept {
+    return view_.find(sv, pos);
+  }
+  constexpr size_type find(Char ch, size_type pos = 0) const noexcept {
+    return view_.find(ch, pos);
+  }
+  constexpr size_type find(
+      Char const* s, size_type pos, size_type count) const {
+    return view_.find(s, pos, count);
+  }
+  constexpr size_type find(Char const* s, size_type pos = 0) const {
+    return view_.find(s, pos);
+  }
+
+  constexpr size_type rfind(view_type sv, size_type pos = npos) const noexcept {
+    return view_.rfind(sv, pos);
+  }
+  constexpr size_type rfind(Char ch, size_type pos = npos) const noexcept {
+    return view_.rfind(ch, pos);
+  }
+  constexpr size_type rfind(
+      Char const* s, size_type pos, size_type count) const {
+    return view_.rfind(s, pos, count);
+  }
+  constexpr size_type rfind(Char const* s, size_type pos = npos) const {
+    return view_.rfind(s, pos);
+  }
+
+  constexpr size_type find_first_of(
+      view_type sv, size_type pos = 0) const noexcept {
+    return view_.find_first_of(sv, pos);
+  }
+  constexpr size_type find_first_of(Char ch, size_type pos = 0) const noexcept {
+    return view_.find_first_of(ch, pos);
+  }
+  constexpr size_type find_first_of(
+      Char const* s, size_type pos, size_type count) const {
+    return view_.find_first_of(s, pos, count);
+  }
+  constexpr size_type find_first_of(Char const* s, size_type pos = 0) const {
+    return view_.find_first_of(s, pos);
+  }
+
+  constexpr size_type find_last_of(
+      view_type sv, size_type pos = npos) const noexcept {
+    return view_.find_last_of(sv, pos);
+  }
+  constexpr size_type find_last_of(
+      Char ch, size_type pos = npos) const noexcept {
+    return view_.find_last_of(ch, pos);
+  }
+  constexpr size_type find_last_of(
+      Char const* s, size_type pos, size_type count) const {
+    return view_.find_last_of(s, pos, count);
+  }
+  constexpr size_type find_last_of(Char const* s, size_type pos = npos) const {
+    return view_.find_last_of(s, pos);
+  }
+
+  constexpr size_type find_first_not_of(
+      view_type sv, size_type pos = 0) const noexcept {
+    return view_.find_first_not_of(sv, pos);
+  }
+  constexpr size_type find_first_not_of(
+      Char ch, size_type pos = 0) const noexcept {
+    return view_.find_first_not_of(ch, pos);
+  }
+  constexpr size_type find_first_not_of(
+      Char const* s, size_type pos, size_type count) const {
+    return view_.find_first_not_of(s, pos, count);
+  }
+  constexpr size_type find_first_not_of(
+      Char const* s, size_type pos = 0) const {
+    return view_.find_first_not_of(s, pos);
+  }
+
+  constexpr size_type find_last_not_of(
+      view_type sv, size_type pos = npos) const noexcept {
+    return view_.find_last_not_of(sv, pos);
+  }
+  constexpr size_type find_last_not_of(
+      Char ch, size_type pos = npos) const noexcept {
+    return view_.find_last_not_of(ch, pos);
+  }
+  constexpr size_type find_last_not_of(
+      Char const* s, size_type pos, size_type count) const {
+    return view_.find_last_not_of(s, pos, count);
+  }
+  constexpr size_type find_last_not_of(
+      Char const* s, size_type pos = npos) const {
+    return view_.find_last_not_of(s, pos);
+  }
+
+  constexpr int compare(view_type sv) const noexcept {
+    return view_.compare(sv);
+  }
+  constexpr int compare(size_type pos1, size_type count1, view_type sv) const {
+    return view_.compare(pos1, count1, sv);
+  }
+  constexpr int compare(
+      size_type pos1,
+      size_type count1,
+      view_type sv,
+      size_type pos2,
+      size_type count2) const {
+    return view_.compare(pos1, count1, sv, pos2, count2);
+  }
+  constexpr int compare(Char const* s) const { return view_.compare(s); }
+  constexpr int compare(size_type pos1, size_type count1, Char const* s) const {
+    return view_.compare(pos1, count1, s);
+  }
+  constexpr int compare(
+      size_type pos1, size_type count1, Char const* s, size_type count2) const {
+    return view_.compare(pos1, count1, s, count2);
+  }
+
+  // Provide c_str() method - this is the key benefit
+  constexpr Char const* c_str() const noexcept { return data(); }
+
+  // Implicit conversion to string_view
+  constexpr operator view_type() const noexcept { return view_; }
+
+  // Provide safe substr that goes to end
+  constexpr basic_cstring_view substr(size_type pos = 0) const {
+    auto sub = view_.substr(pos);
+    return {sub.data(), sub.size()};
+  }
+
+  constexpr void swap(basic_cstring_view& other) noexcept {
+    view_.swap(other.view_);
+  }
+
+#if __cpp_lib_starts_ends_with >= 201711L
+  constexpr bool starts_with(view_type sv) const noexcept {
+    return view_.starts_with(sv);
+  }
+  constexpr bool starts_with(Char ch) const noexcept {
+    return view_.starts_with(ch);
+  }
+  constexpr bool starts_with(Char const* s) const {
+    return view_.starts_with(s);
+  }
+
+  constexpr bool ends_with(view_type sv) const noexcept {
+    return view_.ends_with(sv);
+  }
+  constexpr bool ends_with(Char ch) const noexcept {
+    return view_.ends_with(ch);
+  }
+  constexpr bool ends_with(Char const* s) const { return view_.ends_with(s); }
+#endif
+
+#if defined(__cpp_lib_string_contains) && __cpp_lib_string_contains >= 202011L
+  constexpr bool contains(view_type sv) const noexcept {
+    return view_.contains(sv);
+  }
+  constexpr bool contains(Char ch) const noexcept { return view_.contains(ch); }
+  constexpr bool contains(Char const* s) const { return view_.contains(s); }
+#endif
+
+  // Explicitly delete dangerous operations that could break null-termination
+  constexpr basic_cstring_view substr(size_type pos, size_type len) const =
+      delete;
+  constexpr void remove_suffix(size_type n) = delete;
+
+  friend bool operator==(self lhs, self rhs) noexcept {
+    return view_type(lhs) == view_type(rhs);
+  }
+  friend bool operator!=(self lhs, self rhs) noexcept {
+    return view_type(lhs) != view_type(rhs);
+  }
+  friend bool operator<(self lhs, self rhs) noexcept {
+    return view_type(lhs) < view_type(rhs);
+  }
+  friend bool operator<=(self lhs, self rhs) noexcept {
+    return view_type(lhs) <= view_type(rhs);
+  }
+  friend bool operator>(self lhs, self rhs) noexcept {
+    return view_type(lhs) > view_type(rhs);
+  }
+  friend bool operator>=(self lhs, self rhs) noexcept {
+    return view_type(lhs) >= view_type(rhs);
+  }
+};
+
+using cstring_view = basic_cstring_view<char>;
+
+template <typename Char, typename Traits>
+std::basic_ostream<Char, Traits>& operator<<(
+    std::basic_ostream<Char, Traits>& out,
+    basic_cstring_view<Char, Traits> str) {
+  return out << std::basic_string_view<Char, Traits>(str);
+}
+
+inline namespace literals {
+inline namespace string_literals {
+
+// User-defined literal for cstring_view
+constexpr cstring_view operator""_csv(
+    const char* str, std::size_t len) noexcept {
+  return cstring_view(str, len);
+}
+
+} // namespace string_literals
+} // namespace literals
+
+} // namespace folly
+
+// std::hash specialization
+namespace std {
+template <typename Char, typename Traits>
+struct hash<folly::basic_cstring_view<Char, Traits>>
+    : hash<std::basic_string_view<Char, Traits>> {};
+} // namespace std
+
+namespace fmt {
+template <typename Char, typename Traits>
+struct formatter<folly::basic_cstring_view<Char, Traits>, Char>
+    : formatter<std::basic_string_view<Char, Traits>, Char> {};
+} // namespace fmt
diff --git a/folly/folly/lang/named/Bindings.h b/folly/folly/lang/named/Bindings.h
deleted file mode 100644
--- a/folly/folly/lang/named/Bindings.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <tuple>
-#include <type_traits>
-
-#include <folly/Traits.h>
-#include <folly/lang/Bindings.h>
-
-///
-/// Extends `lang/Bindings.h` (read that first) with keyword-argument syntax:
-///   bound_args{
-///      self_id = make_in_place<MyClass>(), // tag akin to Python's `self`
-///      "x"_id = 5,
-///      "y"_id = mut_ref{y},
-///   }
-///
-/// If the API takes a pack of named bindings, the above syntax is all that a
-/// callers needs to know.  Some API mays require you to wrap the pack with a
-/// custom container like `named_values{...}` (not yet built).
-///
-
-namespace folly::bindings {
-
-namespace ext { // For extension authors -- public details
-
-template <auto Tag, std::derived_from<folly::bindings::ext::bind_info_t> Base>
-struct named_bind_info_t : Base {
-  constexpr const Base& as_base() const { return *this; }
-  using Base::Base;
-  constexpr explicit named_bind_info_t(
-      // Use a constraint to prevent object slicing.
-      std::same_as<Base> auto b)
-      : Base(std::move(b)) {}
-};
-
-namespace detail {
-template <auto Tag>
-struct named_bind_info {
-  template <typename BI>
-    requires(
-        // Use a constraint to prevent object slicing.
-        std::derived_from<BI, bind_info_t>
-        // XXX Future: && a clause to prevent chaining assignments.
-        )
-  constexpr named_bind_info_t<Tag, BI> operator()(BI bi) const {
-    return named_bind_info_t<Tag, BI>{std::move(bi)};
-  }
-};
-} // namespace detail
-
-template <literal_string Str, typename T>
-struct id_arg : ext::merge_update_bound_args<detail::named_bind_info<Str>, T> {
-  using ext::merge_update_bound_args<detail::named_bind_info<Str>, T>::
-      merge_update_bound_args;
-};
-
-struct self_id_t {};
-
-template <typename T>
-struct self_id_arg
-    : ext::merge_update_bound_args<detail::named_bind_info<self_id_t{}>, T> {
-  using ext::merge_update_bound_args<detail::named_bind_info<self_id_t{}>, T>::
-      merge_update_bound_args;
-};
-
-template <auto Tag> // `literal_string<"x">` or `self_id_t{}`
-class identifier {
- private:
-  template <typename T, literal_string Str>
-  static constexpr id_arg<Str, T> make_with_tag(vtag_t<Str>, auto&& ba) {
-    return id_arg<Str, T>{bound_args_unsafe_move::from(std::move(ba))};
-  }
-  template <typename T>
-  static constexpr self_id_arg<T> make_with_tag(
-      vtag_t<self_id_t{}>, auto&& ba) {
-    return self_id_arg<T>{bound_args_unsafe_move::from(std::move(ba))};
-  }
-
- public:
-  // "x"_id = some_binding_modifier{var}
-  constexpr auto operator=(
-      std::derived_from<ext::like_bound_args> auto ba) const {
-    []<typename... BTs>(tag_t<BTs...>) {
-      static_assert(
-          sizeof...(BTs) == 1,
-          "Cannot give the same name to multiple bound args.");
-    }(typename decltype(ba)::binding_list_t{});
-    return make_with_tag<decltype(ba)>(vtag<Tag>, std::move(ba));
-  }
-  // "x"_id = var
-  template <typename T>
-    requires(!std::derived_from<T, ext::like_bound_args>)
-  constexpr auto operator=(T&& t) const {
-    return make_with_tag<T&&>(vtag<Tag>, bound_args{static_cast<T&&>(t)});
-  }
-
-  static inline constexpr auto folly_bindings_identifier_tag = Tag;
-};
-
-} // namespace ext
-
-// These types represents "identifier tag -> storage type" signatures for
-// bindings. Library authors: search for `signature_type`.
-//
-// NB: `id_type` is distinct from `self_id_type` for cleaner error messages.
-// Logically, both of these are `tag_type` (or similar), and the
-// implementation could later be generalized, but for now, this is ok.
-template <typename UnderlyingSig>
-struct self_id_type {
-  using identifier_type = ext::identifier<ext::self_id_t{}>;
-  using storage_type = UnderlyingSig::storage_type;
-};
-template <literal_string Str, typename UnderlyingSig>
-struct id_type {
-  using identifier_type = ext::identifier<Str>;
-  using storage_type = UnderlyingSig::storage_type;
-};
-
-// For brevity, `identifier`s are usually made via custom string literals.
-inline namespace literals {
-inline namespace string_literals {
-template <literal_string Str>
-consteval auto operator""_id() noexcept {
-  return ext::identifier<Str>{};
-}
-} // namespace string_literals
-} // namespace literals
-
-// A special `identifier` tag, which works just like `"x"_id`. It's meant as
-// syntax sugar for "self-reference" scenarios, such as:
-//   - Tagging for the implicit scope/object parameter in `spawn_self_closure`.
-//   - Tagging `this`-pointer-like objects when calling class member functions
-//     with a kwarg calling convention.
-// While it somewhat resembles the role of the `this` pointer, the name
-// `this_id` is ambiguous (which ID?), and doesn't sound well in all use-cases.
-inline constexpr ext::identifier<ext::self_id_t{}> self_id;
-
-namespace ext {
-
-struct no_tag_t {};
-
-template <typename>
-inline constexpr no_tag_t named_bind_info_tag_v{};
-
-template <auto Tag, typename Base>
-inline constexpr auto named_bind_info_tag_v<named_bind_info_t<Tag, Base>> = Tag;
-
-// Named bindings follow the binding policy of the underlying base
-template <auto NBI, typename BindingType>
-// Use a constraint to avoid object slicing
-  requires(!std::is_same_v<
-           const no_tag_t,
-           decltype(named_bind_info_tag_v<decltype(NBI)>)>)
-class binding_policy<ext::binding_t<NBI, BindingType>> {
- private:
-  using underlying = binding_policy<ext::binding_t<NBI.as_base(), BindingType>>;
-
-  template <typename Base>
-  static constexpr auto signature_type_for(
-      const named_bind_info_t<ext::self_id_t{}, Base>&)
-      -> self_id_type<typename underlying::signature_type>;
-
-  template <literal_string Str, typename Base>
-  static constexpr auto signature_type_for(const named_bind_info_t<Str, Base>&)
-      -> id_type<Str, typename underlying::signature_type>;
-
- public:
-  using storage_type = typename underlying::storage_type;
-  using signature_type = decltype(signature_type_for(NBI));
-};
-
-} // namespace ext
-
-} // namespace folly::bindings
diff --git a/folly/folly/logging/AutoTimer.h b/folly/folly/logging/AutoTimer.h
--- a/folly/folly/logging/AutoTimer.h
+++ b/folly/folly/logging/AutoTimer.h
@@ -39,13 +39,18 @@
  * destruction or whenever the log() method is called. For example:
  *
  *   AutoTimer t("Foo() completed");
- *   doWork();
+ *   doWork();  // Takes ~1.2 seconds
  *   t.log("Do work finished");
- *   doMoreWork();
+ *   doMoreWork();  // Takes ~1.1 seconds
  *
  * This would print something like:
  *   "Do work finished in 1.2 seconds"
- *   "Foo() completed in 4.3 seconds"
+ *   "Foo() completed in 1.1 seconds"
+ *
+ * Note that the start of the timer is reset after every call to log(). This
+ * means the destruction message printed after a prior call to log() will
+ * log the time since the last call to log(), not the time since the object
+ * was created.
  *
  * You can customize what you use as the logger and clock. The logger needs
  * to have an operator()(StringPiece, std::chrono::duration<double>) that
diff --git a/folly/folly/logging/CustomLogFormatter.cpp b/folly/folly/logging/CustomLogFormatter.cpp
--- a/folly/folly/logging/CustomLogFormatter.cpp
+++ b/folly/folly/logging/CustomLogFormatter.cpp
@@ -177,8 +177,9 @@
 
           if (FOLLY_UNLIKELY(
                   item == formatKeys.end() || item->key != varName)) {
-            throw std::runtime_error(folly::to<std::string>(
-                "unknown format argument \"", varName, "\""));
+            throw std::runtime_error(
+                folly::to<std::string>(
+                    "unknown format argument \"", varName, "\""));
           }
           output.append(folly::to<std::string>(item->argIndex));
           output.append(p, 1);
diff --git a/folly/folly/logging/FileWriterFactory.cpp b/folly/folly/logging/FileWriterFactory.cpp
--- a/folly/folly/logging/FileWriterFactory.cpp
+++ b/folly/folly/logging/FileWriterFactory.cpp
@@ -52,9 +52,10 @@
     return asyncWriter;
   } else {
     if (maxBufferSize_.has_value()) {
-      throw std::invalid_argument(to<string>(
-          "the \"max_buffer_size\" option is only valid for async file "
-          "handlers"));
+      throw std::invalid_argument(
+          to<string>(
+              "the \"max_buffer_size\" option is only valid for async file "
+              "handlers"));
     }
     return make_shared<ImmediateFileWriter>(std::move(file));
   }
diff --git a/folly/folly/logging/Init.cpp b/folly/folly/logging/Init.cpp
--- a/folly/folly/logging/Init.cpp
+++ b/folly/folly/logging/Init.cpp
@@ -21,7 +21,6 @@
 #include <folly/logging/LogConfig.h>
 #include <folly/logging/LogConfigParser.h>
 #include <folly/logging/LoggerDB.h>
-#include <folly/logging/StreamHandlerFactory.h>
 
 namespace folly {
 
diff --git a/folly/folly/logging/LogConfig.cpp b/folly/folly/logging/LogConfig.cpp
--- a/folly/folly/logging/LogConfig.cpp
+++ b/folly/folly/logging/LogConfig.cpp
@@ -45,10 +45,11 @@
       // completely replacing it.
       auto iter = handlerConfigs_.find(entry.first);
       if (iter == handlerConfigs_.end()) {
-        throw std::invalid_argument(to<std::string>(
-            "cannot update configuration for unknown log handler \"",
-            entry.first,
-            "\""));
+        throw std::invalid_argument(
+            to<std::string>(
+                "cannot update configuration for unknown log handler \"",
+                entry.first,
+                "\""));
       }
       iter->second.update(entry.second);
     }
diff --git a/folly/folly/logging/LogConfigParser.cpp b/folly/folly/logging/LogConfigParser.cpp
--- a/folly/folly/logging/LogConfigParser.cpp
+++ b/folly/folly/logging/LogConfigParser.cpp
@@ -255,7 +255,7 @@
 
   std::unordered_map<string, string> seenCategories;
   std::vector<StringPiece> pieces;
-  folly::split(",", value, pieces);
+  folly::split(',', value, pieces);
   for (const auto& piece : pieces) {
     LogCategoryConfig categoryConfig;
     StringPiece categoryName;
@@ -284,7 +284,7 @@
 
     // Split the configString into level and handler information.
     std::vector<StringPiece> handlerPieces;
-    folly::split(":", configString, handlerPieces);
+    folly::split(':', configString, handlerPieces);
     FOLLY_SAFE_DCHECK(
         !handlerPieces.empty(),
         "folly::split() always returns a list of length 1");
@@ -419,7 +419,7 @@
   optionsStr = trimWhitespace(optionsStr);
   if (!optionsStr.empty()) {
     std::vector<StringPiece> pieces;
-    folly::split(",", optionsStr, pieces);
+    folly::split(',', optionsStr, pieces);
     FOLLY_SAFE_DCHECK(
         !pieces.empty(), "folly::split() always returns a list of length 1");
 
@@ -460,7 +460,7 @@
   // Everything up to the first semicolon specifies log category configs.
   // From then on each section specifies a single LogHandler config.
   std::vector<StringPiece> pieces;
-  folly::split(";", value, pieces);
+  folly::split(';', value, pieces);
   FOLLY_SAFE_DCHECK(
       !pieces.empty(), "folly::split() always returns a list of length 1");
 
diff --git a/folly/folly/logging/LogLevel.cpp b/folly/folly/logging/LogLevel.cpp
--- a/folly/folly/logging/LogLevel.cpp
+++ b/folly/folly/logging/LogLevel.cpp
@@ -93,8 +93,9 @@
         static_cast<unsigned int>(level) >
             (static_cast<uint32_t>(info.max) -
              static_cast<uint32_t>(info.min))) {
-      throw std::range_error(to<string>(
-          "invalid ", info.lowerPrefix, " logger level: ", name.str()));
+      throw std::range_error(
+          to<string>(
+              "invalid ", info.lowerPrefix, " logger level: ", name.str()));
     }
     return info.max - level;
   }
diff --git a/folly/folly/logging/LogStreamProcessor.cpp b/folly/folly/logging/LogStreamProcessor.cpp
--- a/folly/folly/logging/LogStreamProcessor.cpp
+++ b/folly/folly/logging/LogStreamProcessor.cpp
@@ -187,13 +187,14 @@
   //
   // Any other error here is unexpected and we also want to fail hard
   // in that situation too.
-  category_->admitMessage(LogMessage{
-      category_,
-      level_,
-      filename_,
-      lineNumber_,
-      functionName_,
-      extractMessageString(stream_)});
+  category_->admitMessage(
+      LogMessage{
+          category_,
+          level_,
+          filename_,
+          lineNumber_,
+          functionName_,
+          extractMessageString(stream_)});
 }
 
 std::string LogStreamProcessor::extractMessageString(
diff --git a/folly/folly/logging/LogStreamProcessor.h b/folly/folly/logging/LogStreamProcessor.h
--- a/folly/folly/logging/LogStreamProcessor.h
+++ b/folly/folly/logging/LogStreamProcessor.h
@@ -85,7 +85,7 @@
       folly::StringPiece filename,
       unsigned int lineNumber,
       folly::StringPiece functionName,
-      AppendType,
+      AppendType /*unused*/,
       Args&&... args) noexcept
       : LogStreamProcessor(
             category,
@@ -112,7 +112,7 @@
       folly::StringPiece filename,
       unsigned int lineNumber,
       folly::StringPiece functionName,
-      FormatType,
+      FormatType /*unused*/,
       folly::StringPiece fmt,
       Args&&... args) noexcept
       : LogStreamProcessor(
@@ -151,7 +151,7 @@
       folly::StringPiece filename,
       unsigned int lineNumber,
       folly::StringPiece functionName,
-      AppendType,
+      AppendType /*unused*/,
       Args&&... args) noexcept
       : LogStreamProcessor(
             categoryInfo,
@@ -172,7 +172,7 @@
       folly::StringPiece filename,
       unsigned int lineNumber,
       folly::StringPiece functionName,
-      FormatType,
+      FormatType /*unused*/,
       folly::StringPiece fmt,
       Args&&... args) noexcept
       : LogStreamProcessor(
@@ -211,7 +211,7 @@
       folly::StringPiece filename,
       unsigned int lineNumber,
       folly::StringPiece functionName,
-      AppendType) noexcept
+      AppendType /*unused*/) noexcept
       : LogStreamProcessor(
             fileScopeInfo, level, filename, lineNumber, functionName, APPEND) {}
   template <typename... Args>
@@ -223,7 +223,7 @@
       folly::StringPiece filename,
       unsigned int lineNumber,
       folly::StringPiece functionName,
-      AppendType,
+      AppendType /*unused*/,
       Args&&... args) noexcept
       : LogStreamProcessor(
             fileScopeInfo,
@@ -242,7 +242,7 @@
       folly::StringPiece filename,
       unsigned int lineNumber,
       folly::StringPiece functionName,
-      FormatType,
+      FormatType /*unused*/,
       folly::StringPiece fmt,
       Args&&... args) noexcept
       : LogStreamProcessor(
@@ -305,6 +305,19 @@
 
   std::string extractMessageString(LogStream& stream) noexcept;
 
+  [[FOLLY_ATTR_GNU_COLD]] static std::string createLogStringCatch() {
+    // This most likely means there was some error converting the
+    // arguments to strings.  Handle the exception here, rather than
+    // letting it propagate up, since callers generally do not expect log
+    // statements to throw.
+    //
+    // Just log an error message letting indicating that something went
+    // wrong formatting the log message.
+    return folly::to<std::string>(
+        "error constructing log message: ",
+        exceptionStr(std::current_exception()));
+  }
+
   /**
    * Construct a log message string using folly::to<std::string>()
    *
@@ -315,28 +328,18 @@
    */
   template <typename... Args>
   FOLLY_NOINLINE std::string createLogString(Args&&... args) noexcept {
-    return folly::catch_exception<const std::exception&>(
+    return folly::catch_exception(
         [&] { return folly::to<std::string>(std::forward<Args>(args)...); },
-        [&](const std::exception& ex) {
-          // This most likely means there was some error converting the
-          // arguments to strings.  Handle the exception here, rather than
-          // letting it propagate up, since callers generally do not expect log
-          // statements to throw.
-          //
-          // Just log an error message letting indicating that something went
-          // wrong formatting the log message.
-          return folly::to<std::string>(
-              "error constructing log message: ", exceptionStr(ex));
-        });
+        &createLogStringCatch);
   }
 
-  FOLLY_NOINLINE std::string vformatLogString(
+  static FOLLY_NOINLINE std::string vformatLogString(
       folly::StringPiece fmt, fmt::format_args args, bool& failed) noexcept {
-    return folly::catch_exception<const std::exception&>(
+    return folly::catch_exception(
         [&] {
           return fmt::vformat(fmt::string_view(fmt.data(), fmt.size()), args);
         },
-        [&](const std::exception& ex) {
+        [&]() {
           // This most likely means that the caller had a bug in their format
           // string/arguments.  Handle the exception here, rather than letting
           // it propagate up, since callers generally do not expect log
@@ -347,7 +350,7 @@
           failed = true;
           std::string result;
           result.append("error formatting log message: ");
-          result.append(exceptionStr(ex).c_str());
+          result.append(exceptionStr(std::current_exception()).c_str());
           result.append("; format string: \"");
           result.append(fmt.data(), fmt.size());
           result.append("\", arguments: ");
@@ -424,7 +427,7 @@
    * eliminated by the compiler, leaving only the LogStreamProcessor destructor
    * invocation, which cannot be eliminated.
    */
-  void operator&(std::ostream&) noexcept {}
+  void operator&(std::ostream& /*unused*/) noexcept {}
 };
 
 template <>
@@ -461,7 +464,7 @@
  * out how to format it.
  */
 // clang-format off
-inline void logDisabledHelper(std::false_type) noexcept {}
+inline void logDisabledHelper(std::false_type /*unused*/) noexcept {}
 [[noreturn]] void logDisabledHelper(std::true_type) noexcept;
 // clang-format on
 } // namespace folly
diff --git a/folly/folly/logging/Logger.cpp b/folly/folly/logging/Logger.cpp
--- a/folly/folly/logging/Logger.cpp
+++ b/folly/folly/logging/Logger.cpp
@@ -16,10 +16,6 @@
 
 #include <folly/logging/Logger.h>
 
-#include <ostream>
-
-#include <folly/Conv.h>
-#include <folly/logging/LogMessage.h>
 #include <folly/logging/LoggerDB.h>
 
 namespace folly {
diff --git a/folly/folly/logging/LoggerDB.cpp b/folly/folly/logging/LoggerDB.cpp
--- a/folly/folly/logging/LoggerDB.cpp
+++ b/folly/folly/logging/LoggerDB.cpp
@@ -22,6 +22,7 @@
 #include <folly/Conv.h>
 #include <folly/FileUtil.h>
 #include <folly/String.h>
+#include <folly/container/Reserve.h>
 #include <folly/logging/LogCategory.h>
 #include <folly/logging/LogConfig.h>
 #include <folly/logging/LogHandler.h>
@@ -222,8 +223,9 @@
     } else {
       // This configuration is intended to update an existing LogHandler
       if (!oldHandler) {
-        throw std::invalid_argument(to<std::string>(
-            "cannot update unknown log handler \"", entry.first, "\""));
+        throw std::invalid_argument(
+            to<std::string>(
+                "cannot update unknown log handler \"", entry.first, "\""));
       }
 
       updatedConfig = oldHandler->getConfig();
@@ -231,10 +233,11 @@
         // This normally should not happen unless someone improperly manually
         // constructed a LogHandler object.  All existing LogHandler objects
         // should indicate their type.
-        throw std::invalid_argument(to<std::string>(
-            "existing log handler \"",
-            entry.first,
-            "\" is missing type information"));
+        throw std::invalid_argument(
+            to<std::string>(
+                "existing log handler \"",
+                entry.first,
+                "\" is missing type information"));
       }
       updatedConfig.update(entry.second);
       handlerConfig = &updatedConfig;
@@ -243,8 +246,11 @@
     // Look up the LogHandlerFactory
     auto factoryIter = handlerInfo->factories.find(handlerConfig->type.value());
     if (factoryIter == handlerInfo->factories.end()) {
-      throw std::invalid_argument(to<std::string>(
-          "unknown log handler type \"", handlerConfig->type.value(), "\""));
+      throw std::invalid_argument(
+          to<std::string>(
+              "unknown log handler type \"",
+              handlerConfig->type.value(),
+              "\""));
     }
 
     // Create the new log handler
@@ -264,13 +270,14 @@
       // bad configuration options.  It is useful to update the exception
       // message to include the name of the log handler we were trying to
       // update or create.
-      throw std::invalid_argument(to<string>(
-          "error ",
-          oldHandler ? "updating" : "creating",
-          " log handler \"",
-          entry.first,
-          "\": ",
-          exceptionStr(ex)));
+      throw std::invalid_argument(
+          to<string>(
+              "error ",
+              oldHandler ? "updating" : "creating",
+              " log handler \"",
+              entry.first,
+              "\": ",
+              exceptionStr(ex)));
     }
     handlerInfo->handlers[entry.first] = handler;
     (*handlers)[entry.first] = handler;
@@ -285,12 +292,13 @@
     for (const auto& handlerName : entry.second.handlers.value()) {
       auto iter = handlers->find(handlerName);
       if (iter == handlers->end()) {
-        throw std::invalid_argument(to<std::string>(
-            "unknown log handler \"",
-            handlerName,
-            "\" configured for log category \"",
-            entry.first,
-            "\""));
+        throw std::invalid_argument(
+            to<std::string>(
+                "unknown log handler \"",
+                handlerName,
+                "\" configured for log category \"",
+                entry.first,
+                "\""));
       }
     }
   }
@@ -330,17 +338,19 @@
     StringPiece categoryName,
     const std::vector<std::string>& categoryHandlerNames) {
   std::vector<std::shared_ptr<LogHandler>> catHandlers;
+  folly::grow_capacity_by(catHandlers, categoryHandlerNames.size());
   for (const auto& handlerName : categoryHandlerNames) {
     auto iter = handlerMap.find(handlerName);
     if (iter == handlerMap.end()) {
       // This really shouldn't be possible; the checks in startConfigUpdate()
       // should have already bailed out if there was an unknown handler.
-      throw std::invalid_argument(to<std::string>(
-          "bug: unknown log handler \"",
-          handlerName,
-          "\" configured for log category \"",
-          categoryName,
-          "\""));
+      throw std::invalid_argument(
+          to<std::string>(
+              "bug: unknown log handler \"",
+              handlerName,
+              "\" configured for log category \"",
+              categoryName,
+              "\""));
     }
     catHandlers.push_back(iter->second);
   }
@@ -535,8 +545,11 @@
   } else {
     auto ret = handlerInfo->factories.emplace(type.str(), std::move(factory));
     if (!ret.second) {
-      throw std::range_error(to<std::string>(
-          "a LogHandlerFactory for the type \"", type, "\" already exists"));
+      throw std::range_error(
+          to<std::string>(
+              "a LogHandlerFactory for the type \"",
+              type,
+              "\" already exists"));
     }
   }
 }
@@ -621,8 +634,9 @@
   void push(ContextCallback callback) {
     auto end = end_.load(std::memory_order_relaxed);
     if (end == block_.end()) {
-      folly::throw_exception(std::length_error(
-          "Exceeding limit for the number of pushed context callbacks."));
+      folly::throw_exception(
+          std::length_error(
+              "Exceeding limit for the number of pushed context callbacks."));
     }
     *end = std::move(callback);
     end_.store(std::next(end), std::memory_order_release);
diff --git a/folly/folly/logging/ObjectToString.h b/folly/folly/logging/ObjectToString.h
--- a/folly/folly/logging/ObjectToString.h
+++ b/folly/folly/logging/ObjectToString.h
@@ -89,7 +89,7 @@
 using folly::toAppend;
 
 template <typename Arg>
-auto appendObjectToString(std::string& str, const Arg* arg, int)
+auto appendObjectToString(std::string& str, const Arg* arg, int /*unused*/)
     -> decltype(toAppend(std::declval<Arg>(), std::declval<std::string*>()), std::declval<void>()) {
   ::folly::catch_exception(
       [&] { toAppend(*arg, &str); },
@@ -101,7 +101,8 @@
 }
 
 template <typename Arg>
-inline void appendObjectToString(std::string& str, const Arg* arg, long) {
+inline void appendObjectToString(
+    std::string& str, const Arg* arg, long /*unused*/) {
   ::folly::logging::appendRawObjectInfo(str, arg);
 }
 } // namespace detail
diff --git a/folly/folly/logging/StandardLogHandlerFactory.cpp b/folly/folly/logging/StandardLogHandlerFactory.cpp
--- a/folly/folly/logging/StandardLogHandlerFactory.cpp
+++ b/folly/folly/logging/StandardLogHandlerFactory.cpp
@@ -40,10 +40,11 @@
       if (expectedLogThreadName.hasValue()) {
         log_thread_name_ = expectedLogThreadName.value();
       } else {
-        throw std::invalid_argument(to<string>(
-            "unknown log_thread_name type \"",
-            value,
-            "\". Needs to be true/false or 1/0"));
+        throw std::invalid_argument(
+            to<string>(
+                "unknown log_thread_name type \"",
+                value,
+                "\". Needs to be true/false or 1/0"));
       }
       return true;
     }
@@ -75,10 +76,11 @@
       } else if (value == "never") {
         colored_ = NEVER;
       } else {
-        throw std::invalid_argument(to<string>(
-            "unknown colored type \"",
-            value,
-            "\". Needs to be always/never/auto"));
+        throw std::invalid_argument(
+            to<string>(
+                "unknown colored type \"",
+                value,
+                "\". Needs to be always/never/auto"));
       }
       return true;
     }
@@ -144,8 +146,9 @@
       handled |= formatterFactory->processOption(entry.first, entry.second);
       handled |= writerFactory->processOption(entry.first, entry.second);
     } catch (const std::exception& ex) {
-      errors.push_back(to<string>(
-          "error processing option \"", entry.first, "\": ", ex.what()));
+      errors.push_back(
+          to<string>(
+              "error processing option \"", entry.first, "\": ", ex.what()));
       continue;
     }
 
@@ -156,11 +159,12 @@
       try {
         logLevel = stringToLogLevel(entry.second);
       } catch (const std::exception& ex) {
-        errors.push_back(to<string>(
-            "unable to parse value for option \"",
-            entry.first,
-            "\": ",
-            ex.what()));
+        errors.push_back(
+            to<string>(
+                "unable to parse value for option \"",
+                entry.first,
+                "\": ",
+                ex.what()));
       }
       handled = true;
     } else if (entry.first == "sync_level") {
@@ -168,11 +172,12 @@
       try {
         syncLevel = stringToLogLevel(entry.second);
       } catch (const std::exception& ex) {
-        errors.push_back(to<string>(
-            "unable to parse value for option \"",
-            entry.first,
-            "\": ",
-            ex.what()));
+        errors.push_back(
+            to<string>(
+                "unable to parse value for option \"",
+                entry.first,
+                "\": ",
+                ex.what()));
       }
       handled = true;
     }
diff --git a/folly/folly/logging/StreamHandlerFactory.cpp b/folly/folly/logging/StreamHandlerFactory.cpp
--- a/folly/folly/logging/StreamHandlerFactory.cpp
+++ b/folly/folly/logging/StreamHandlerFactory.cpp
@@ -42,8 +42,11 @@
   } else if (stream_ == "stdout") {
     outputFile = File{STDOUT_FILENO, /* ownsFd */ false};
   } else {
-    throw std::invalid_argument(to<std::string>(
-        "unknown stream \"", stream_, "\": expected one of stdout or stderr"));
+    throw std::invalid_argument(
+        to<std::string>(
+            "unknown stream \"",
+            stream_,
+            "\": expected one of stdout or stderr"));
   }
 
   return fileWriterFactory_.createWriter(std::move(outputFile));
diff --git a/folly/folly/logging/xlog.cpp b/folly/folly/logging/xlog.cpp
--- a/folly/folly/logging/xlog.cpp
+++ b/folly/folly/logging/xlog.cpp
@@ -19,7 +19,6 @@
 #include <type_traits>
 #include <unordered_map>
 
-#include <folly/Synchronized.h>
 #include <folly/portability/PThread.h>
 
 namespace folly {
diff --git a/folly/folly/logging/xlog.h b/folly/folly/logging/xlog.h
--- a/folly/folly/logging/xlog.h
+++ b/folly/folly/logging/xlog.h
@@ -130,15 +130,15 @@
  *
  * Note that this is threadsafe.
  */
-#define XLOG_EVERY_MS(level, ms, ...)                                  \
-  XLOG_IF(                                                             \
-      level,                                                           \
-      [__folly_detail_xlog_ms = ms] {                                  \
-        static ::folly::logging::IntervalRateLimiter                   \
-            folly_detail_xlog_limiter(                                 \
-                1, std::chrono::milliseconds(__folly_detail_xlog_ms)); \
-        return folly_detail_xlog_limiter.check();                      \
-      }(),                                                             \
+#define XLOG_EVERY_MS(level, ms, ...)                                 \
+  XLOG_IF(                                                            \
+      level,                                                          \
+      [_folly_detail_xlog_ms = ms] {                                  \
+        static ::folly::logging::IntervalRateLimiter                  \
+            folly_detail_xlog_limiter(                                \
+                1, std::chrono::milliseconds(_folly_detail_xlog_ms)); \
+        return folly_detail_xlog_limiter.check();                     \
+      }(),                                                            \
       ##__VA_ARGS__)
 
 /**
@@ -147,16 +147,16 @@
  *
  * Note that this is threadsafe.
  */
-#define XLOG_EVERY_MS_IF(level, cond, ms, ...)                               \
-  XLOG_IF(                                                                   \
-      level,                                                                 \
-      (cond) &&                                                              \
-          [__folly_detail_xlog_ms = ms] {                                    \
-            static ::folly::logging::IntervalRateLimiter                     \
-                folly_detail_xlog_limiter(                                   \
-                    1, ::std::chrono::milliseconds(__folly_detail_xlog_ms)); \
-            return folly_detail_xlog_limiter.check();                        \
-          }(),                                                               \
+#define XLOG_EVERY_MS_IF(level, cond, ms, ...)                              \
+  XLOG_IF(                                                                  \
+      level,                                                                \
+      (cond) &&                                                             \
+          [_folly_detail_xlog_ms = ms] {                                    \
+            static ::folly::logging::IntervalRateLimiter                    \
+                folly_detail_xlog_limiter(                                  \
+                    1, ::std::chrono::milliseconds(_folly_detail_xlog_ms)); \
+            return folly_detail_xlog_limiter.check();                       \
+          }(),                                                              \
       ##__VA_ARGS__)
 
 /**
@@ -165,16 +165,16 @@
  *
  * Note that this is threadsafe.
  */
-#define XLOG_EVERY_MS_OR(level, cond, ms, ...)                               \
-  XLOG_IF(                                                                   \
-      level,                                                                 \
-      (cond) ||                                                              \
-          [__folly_detail_xlog_ms = ms] {                                    \
-            static ::folly::logging::IntervalRateLimiter                     \
-                folly_detail_xlog_limiter(                                   \
-                    1, ::std::chrono::milliseconds(__folly_detail_xlog_ms)); \
-            return folly_detail_xlog_limiter.check();                        \
-          }(),                                                               \
+#define XLOG_EVERY_MS_OR(level, cond, ms, ...)                              \
+  XLOG_IF(                                                                  \
+      level,                                                                \
+      (cond) ||                                                             \
+          [_folly_detail_xlog_ms = ms] {                                    \
+            static ::folly::logging::IntervalRateLimiter                    \
+                folly_detail_xlog_limiter(                                  \
+                    1, ::std::chrono::milliseconds(_folly_detail_xlog_ms)); \
+            return folly_detail_xlog_limiter.check();                       \
+          }(),                                                              \
       ##__VA_ARGS__)
 
 /**
@@ -183,17 +183,17 @@
  *
  * Note that this is threadsafe.
  */
-#define XLOGF_EVERY_MS_IF(level, cond, ms, fmt, ...)                         \
-  XLOGF_IF(                                                                  \
-      level,                                                                 \
-      (cond) &&                                                              \
-          [__folly_detail_xlog_ms = ms] {                                    \
-            static ::folly::logging::IntervalRateLimiter                     \
-                folly_detail_xlog_limiter(                                   \
-                    1, ::std::chrono::milliseconds(__folly_detail_xlog_ms)); \
-            return folly_detail_xlog_limiter.check();                        \
-          }(),                                                               \
-      fmt,                                                                   \
+#define XLOGF_EVERY_MS_IF(level, cond, ms, fmt, ...)                        \
+  XLOGF_IF(                                                                 \
+      level,                                                                \
+      (cond) &&                                                             \
+          [_folly_detail_xlog_ms = ms] {                                    \
+            static ::folly::logging::IntervalRateLimiter                    \
+                folly_detail_xlog_limiter(                                  \
+                    1, ::std::chrono::milliseconds(_folly_detail_xlog_ms)); \
+            return folly_detail_xlog_limiter.check();                       \
+          }(),                                                              \
+      fmt,                                                                  \
       ##__VA_ARGS__)
 
 /**
@@ -211,17 +211,17 @@
  *
  * Note that this is threadsafe.
  */
-#define XLOGF_EVERY_MS_OR(level, cond, ms, fmt, ...)                         \
-  XLOGF_IF(                                                                  \
-      level,                                                                 \
-      (cond) ||                                                              \
-          [__folly_detail_xlog_ms = ms] {                                    \
-            static ::folly::logging::IntervalRateLimiter                     \
-                folly_detail_xlog_limiter(                                   \
-                    1, ::std::chrono::milliseconds(__folly_detail_xlog_ms)); \
-            return folly_detail_xlog_limiter.check();                        \
-          }(),                                                               \
-      fmt,                                                                   \
+#define XLOGF_EVERY_MS_OR(level, cond, ms, fmt, ...)                        \
+  XLOGF_IF(                                                                 \
+      level,                                                                \
+      (cond) ||                                                             \
+          [_folly_detail_xlog_ms = ms] {                                    \
+            static ::folly::logging::IntervalRateLimiter                    \
+                folly_detail_xlog_limiter(                                  \
+                    1, ::std::chrono::milliseconds(_folly_detail_xlog_ms)); \
+            return folly_detail_xlog_limiter.check();                       \
+          }(),                                                              \
+      fmt,                                                                  \
       ##__VA_ARGS__)
 
 namespace folly {
@@ -292,7 +292,7 @@
 
 /**
  * Similar to XLOG(...) except it logs a message if the condition predicate
- * evalutes to true or approximately every @param n invocations
+ * evaluates to true or approximately every @param n invocations
  *
  * See concurrency discussion for XLOG_EVERY_N which applies here as well.
  */
@@ -326,7 +326,7 @@
 
 /**
  * Similar to XLOGF(...) except it logs a message if the condition predicate
- * evalutes to true or approximately every @param n invocations
+ * evaluates to true or approximately every @param n invocations
  *
  * See concurrency discussion for XLOG_EVERY_N which applies here as well.
  */
@@ -459,6 +459,22 @@
       ##__VA_ARGS__)
 
 /**
+ * Similar to XLOGF(...) except only log a message the first n times, exactly.
+ *
+ * The internal counter is process-global and threadsafe and exchanges are
+ * atomic.
+ */
+#define XLOGF_FIRST_N(level, n, fmt, ...)                                      \
+  XLOGF_IF(                                                                    \
+      level,                                                                   \
+      [&] {                                                                    \
+        struct folly_detail_xlog_tag {};                                       \
+        return ::folly::detail::xlogFirstNExactImpl<folly_detail_xlog_tag>(n); \
+      }(),                                                                     \
+      fmt,                                                                     \
+      ##__VA_ARGS__)
+
+/**
  * FOLLY_XLOG_STRIP_PREFIXES can be defined to a string containing a
  * colon-separated list of directory prefixes to strip off from the filename
  * before using it to compute the log category name.
@@ -528,6 +544,7 @@
  *   skipped with just a single check of the LogLevel.
  */
 #define XLOG_ACTUAL_IMPL(level, cond, always_fatal, type, ...)              \
+  /* NOLINTNEXTLINE(readability-simplify-boolean-expr) */                   \
   (!XLOG_IS_ON_IMPL(level) || !(cond))                                      \
       ? ::folly::logDisabledHelper(::std::bool_constant<always_fatal>{})    \
       : ::folly::LogStreamVoidify<::folly::isLogLevelFatal(level)>{} &      \
@@ -595,6 +612,7 @@
  */
 #define XLOG_IS_ON_IMPL_HELPER(level)                           \
   ([] {                                                         \
+    /* NOLINTNEXTLINE(misc-const-correctness) */                \
     static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE>       \
         folly_detail_xlog_level;                                \
     constexpr auto* folly_detail_xlog_filename = XLOG_FILENAME; \
@@ -898,7 +916,7 @@
     }
 
     // If we are still here we the file-scope log level either needs to be
-    // initalized, or the log level check legitimately passed.
+    // initialized, or the log level check legitimately passed.
     auto currentLevel =
         loadLevelFull(categoryName, isOverridden, fileScopeInfo);
     return levelToCheck >= currentLevel;
diff --git a/folly/folly/memcpy.S b/folly/folly/memcpy.S
new file mode 100644
--- /dev/null
+++ b/folly/folly/memcpy.S
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * __folly_memcpy
+ *
+ * This implementation of memcpy acts as a memmove: while overlapping copies
+ * are undefined in memcpy, in some implementations they're the same function and
+ * legacy programs rely on this behavior.
+ *
+ * For sizes up to 256 all source data is first read into registers and then written:
+ * - n <=  16: overlapping movs
+ * - n <=  32: overlapping unaligned 16-byte SSE XMM load/stores
+ * - n <= 256: overlapping unaligned 32-byte AVX YMM load/stores
+ *
+ * For n > 256:
+ * - for src >= dst, forward copy:
+ *   - if n >= REP_MOVSB_THRESHOLD, use rep movsb
+ *   - otherwise, copy in 128 byte batches
+ * - for src < dst && (src + n) <= dst, forward copy:
+ *   - if n >= REP_MOVSB_THRESHOLD, use rep movsb
+ *   - otherwise, copy in 128 byte batches
+ * - for src < dst && (src + n) > dst, backward copy in 128 byte batches:
+ *   - unaligned load the first 4 x 32 bytes & last 32 bytes
+ *   - backward copy (unaligned load + aligned stores) 4 x 32 bytes at a time
+ *   - unaligned store the first 4 x 32 bytes & last 32 bytes
+ */
+
+#if defined(__AVX2__)
+
+#define REP_MOVSB_THRESHOLD $1024
+
+        .file       "memcpy.S"
+        .section    .text,"ax"
+
+        .type       __folly_memcpy_short, @function
+__folly_memcpy_short:
+        .cfi_startproc
+
+.L_GE1_LE7:
+        cmp         $1, %rdx
+        je          .L_EQ1
+
+        cmp         $4, %rdx
+        jae         .L_GE4_LE7
+
+.L_GE2_LE3:
+        movw        (%rsi), %r8w
+        movw        -2(%rsi,%rdx), %r9w
+        movw        %r8w, (%rdi)
+        movw        %r9w, -2(%rdi,%rdx)
+        ret
+
+        .align      2
+.L_EQ1:
+        movb        (%rsi), %r8b
+        movb        %r8b, (%rdi)
+        ret
+
+        // Aligning the target of a jump to an even address has a measurable
+        // speedup in microbenchmarks.
+        .align      2
+.L_GE4_LE7:
+        movl        (%rsi), %r8d
+        movl        -4(%rsi,%rdx), %r9d
+        movl        %r8d, (%rdi)
+        movl        %r9d, -4(%rdi,%rdx)
+        ret
+
+        .cfi_endproc
+        .size       __folly_memcpy_short, .-__folly_memcpy_short
+
+// memcpy is an alternative entrypoint into the function named __folly_memcpy.
+// The compiler is able to call memcpy since the name is global while
+// stacktraces will show __folly_memcpy since that is the name of the function.
+// This is intended to aid in debugging by making it obvious which version of
+// memcpy is being used.
+        .align      64
+        .globl      __folly_memcpy
+        .type       __folly_memcpy, @function
+
+__folly_memcpy:
+        .cfi_startproc
+
+        mov         %rdi, %rax    # return: $rdi
+
+        test        %rdx, %rdx
+        je          .L_EQ0
+
+        cmp         $8, %rdx
+        jb          .L_GE1_LE7
+
+.L_GE8:
+        cmp         $32, %rdx
+        ja          .L_GE33
+
+.L_GE8_LE32:
+        cmp         $16, %rdx
+        ja          .L_GE17_LE32
+
+.L_GE8_LE16:
+        mov         (%rsi), %r8
+        mov         -8(%rsi,%rdx), %r9
+        mov         %r8, (%rdi)
+        mov         %r9, -8(%rdi,%rdx)
+.L_EQ0:
+        ret
+
+        .align      2
+.L_GE17_LE32:
+        movdqu      (%rsi), %xmm0
+        movdqu      -16(%rsi,%rdx), %xmm1
+        movdqu      %xmm0, (%rdi)
+        movdqu      %xmm1, -16(%rdi,%rdx)
+        ret
+
+        .align      2
+.L_GE193_LE256:
+        vmovdqu     %ymm3, 96(%rdi)
+        vmovdqu     %ymm4, -128(%rdi,%rdx)
+
+.L_GE129_LE192:
+        vmovdqu     %ymm2, 64(%rdi)
+        vmovdqu     %ymm5, -96(%rdi,%rdx)
+
+.L_GE65_LE128:
+        vmovdqu     %ymm1, 32(%rdi)
+        vmovdqu     %ymm6, -64(%rdi,%rdx)
+
+.L_GE33_LE64:
+        vmovdqu     %ymm0, (%rdi)
+        vmovdqu     %ymm7, -32(%rdi,%rdx)
+
+        vzeroupper
+        ret
+
+        .align      2
+.L_GE33:
+        cmp         $256, %rdx
+        ja          .L_GE257
+
+.L_GE33_LE256:
+        vmovdqu     (%rsi), %ymm0
+        vmovdqu     -32(%rsi,%rdx), %ymm7
+
+        cmp         $64, %rdx
+        jbe         .L_GE33_LE64
+
+        vmovdqu     32(%rsi), %ymm1
+        vmovdqu     -64(%rsi,%rdx), %ymm6
+
+        cmp         $128, %rdx
+        jbe         .L_GE65_LE128
+
+        vmovdqu     64(%rsi), %ymm2
+        vmovdqu     -96(%rsi,%rdx), %ymm5
+
+        cmp         $192, %rdx
+        jbe         .L_GE129_LE192
+
+        vmovdqu     96(%rsi), %ymm3
+        vmovdqu     -128(%rsi,%rdx), %ymm4
+
+        cmp         $256, %rdx
+        jbe         .L_GE193_LE256
+
+.L_GE257:
+        cmp         %rdi, %rsi
+        jae         .L_COPY_FORWARD     # if src >= dst, copy forward
+
+        lea         (%rsi,%rdx), %r10   # r10 = (src + n)
+        cmp         %rdi, %r10          # if src < dst && (src + n) > dst, copy backward
+        ja          .L_OVERLAP_BWD      # otherwise, fall through to copy forward
+
+.L_COPY_FORWARD:
+        mov         %rdx, %rcx          # rcx is the copy length n
+        cmp         REP_MOVSB_THRESHOLD, %rdx
+        jb          .L_COPY_FORWARD_WITH_LOOP
+
+.L_COPY_FORWARD_WITH_REP_MOVSB:
+        rep movsb
+        ret
+
+.L_COPY_FORWARD_WITH_LOOP:
+        vmovdqu     -32(%rsi,%rdx), %ymm4
+        xor         %r8, %r8            # r8 is the length that has been copied
+        shr         $7, %rcx            # rcx = n/128
+        shl         $7, %rcx            # rcx is the length that should be copied by the loop
+
+        .align 16
+.L_COPY_FORWARD_LOOP_BODY:
+        vmovdqu     (%rsi,%r8), %ymm0
+        vmovdqu     32(%rsi,%r8), %ymm1
+        vmovdqu     64(%rsi,%r8), %ymm2
+        vmovdqu     96(%rsi,%r8), %ymm3
+        vmovdqu     %ymm0, (%rdi,%r8)
+        vmovdqu     %ymm1, 32(%rdi,%r8)
+        vmovdqu     %ymm2, 64(%rdi,%r8)
+        vmovdqu     %ymm3, 96(%rdi,%r8)
+        add         $128, %r8
+        cmp         %rcx, %r8
+        jb          .L_COPY_FORWARD_LOOP_BODY
+
+        mov         %rdx, %rcx          # rcx is the original length n
+        sub         %r8, %rdx           # rdx is the tail length
+        cmp         $32, %rdx
+        jbe         .L_TAIL_LE32
+        add         %r8, %rsi           # rsi is the tail of src
+        add         %r8, %rdi           # rdi is the tail of dst
+        jmp         .L_GE33_LE256
+
+.L_TAIL_LE32:
+        vmovdqu     %ymm4, -32(%rdi,%rcx)
+        vzeroupper
+        ret
+
+.L_OVERLAP_BWD:
+        # Save last 32 bytes.
+        vmovdqu     -32(%rsi, %rdx), %ymm8
+        lea         -32(%rdi, %rdx), %r9
+        vmovdqu     (%rsi),   %ymm0     # save the first 128 bytes to ymm0/1/2/3
+        vmovdqu     32(%rsi), %ymm1
+        vmovdqu     64(%rsi), %ymm2
+        vmovdqu     96(%rsi), %ymm3
+        // %r8 is the end condition for the loop.
+        lea         128(%rsi), %r8
+
+        // Align %rdi+%rdx (destination end) to a 32 byte boundary.
+        // %rcx = (%rdi + %rdx - 32) & 31
+        mov         %r9, %rcx
+        and         $31, %rcx
+        // Set %rsi & %rdi to the end of the 32 byte aligned range.
+        sub         %rcx, %rdx
+        add         %rdx, %rsi
+        add         %rdx, %rdi
+
+        .align 16
+.L_OVERLAP_BWD_ALIGNED_DST_LOOP:
+        vmovdqu      -32(%rsi), %ymm4
+        vmovdqu      -64(%rsi), %ymm5
+        vmovdqu      -96(%rsi), %ymm6
+        vmovdqu     -128(%rsi), %ymm7
+        sub         $128, %rsi
+
+        vmovdqa     %ymm4,  -32(%rdi)
+        vmovdqa     %ymm5,  -64(%rdi)
+        vmovdqa     %ymm6,  -96(%rdi)
+        vmovdqa     %ymm7, -128(%rdi)
+        sub         $128, %rdi
+
+        cmp         %r8, %rsi
+        ja          .L_OVERLAP_BWD_ALIGNED_DST_LOOP
+
+        vmovdqu     %ymm0,   (%rax)  // %rax == the original unaligned %rdi
+        vmovdqu     %ymm1, 32(%rax)
+        vmovdqu     %ymm2, 64(%rax)
+        vmovdqu     %ymm3, 96(%rax)
+        vmovdqu     %ymm8, (%r9)
+
+        vzeroupper
+	ret
+
+        .cfi_endproc
+        .size       __folly_memcpy, .-__folly_memcpy
+
+#ifdef FOLLY_MEMCPY_IS_MEMCPY
+        .weak       memcpy
+        memcpy = __folly_memcpy
+
+        .weak       memmove
+        memmove = __folly_memcpy
+#endif
+
+        .ident "GCC: (GNU) 4.8.2"
+
+#endif
+#ifdef __linux__
+        .section .note.GNU-stack,"",@progbits
+#endif
diff --git a/folly/folly/memcpy_select_aarch64.cpp b/folly/folly/memcpy_select_aarch64.cpp
deleted file mode 100644
--- a/folly/folly/memcpy_select_aarch64.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * How on earth does this work?
- *
- * We rely on a handful of functions from the Arm Optimised Routines
- * project. Specifically, there are implementations for common operations
- * like memcpy for various combinations of aarch64 features.
- *
- * The minor challenge we have is that older compilers don't support newer
- * extensions at all, so we try to detect known-broken cases in the same way
- * as the Optimised Routines library does.
- *
- * That aside, each of the optimised routines implementations turns on the
- * architecture extensions it needs in the source file, which is how we get
- * SVE support without having requested it on our command-line.
- *
- * At runtime we use a GNU IFUNC with a resolver to choose the most performant
- * implementation based on the CPU features presented to us in the aux data
- * by the kernel.
- *
- * Finally, we alias memcpy and memmove to our implementations when instructed
- * to do so.
- *
- * In future we can add the mops (memory operations) extension implementation,
- * but that's very new and doesn't yet have broad compiler or auxval support.
- *
- * A note on the ifunc resolver ABI: This is almost entirely undocumented. On
- * aarch64 the value of AUX_HWCAP is passed as the first argument to the
- * function. There's supposed to be an extension that passes AUX_HWWCAP2 as
- * the second argument, but I'm struggling to track down when that was
- * implemented or how to detect it. This is all important because we cannot
- * call any library functions (like getauxval) in a resolver function, and
- * it's unsafe to query ISAR registers without having checked AUX_HWCAP to
- * see if those are callable by userspace.
- */
-
-#include <cstddef>
-#include <cstdint>
-
-#if defined(__linux__) && defined(__aarch64__)
-
-#include <asm/hwcap.h> // @manual
-
-#if __has_include(<sys/ifunc.h>)
-#include <sys/ifunc.h>
-#endif
-
-#if !defined(HWCAP2_MOPS)
-#define HWCAP2_MOPS (1UL << 43)
-#endif
-
-extern "C" {
-
-void* __folly_memcpy_aarch64(void* dst, const void* src, std::size_t size);
-void* __folly_memcpy_aarch64_mops(void* dst, const void* src, std::size_t size);
-void* __folly_memcpy_aarch64_simd(void* dst, const void* src, std::size_t size);
-void* __folly_memcpy_aarch64_sve(void* dst, const void* src, std::size_t size);
-
-void* __folly_memmove_aarch64(void* dst, const void* src, std::size_t len);
-void* __folly_memmove_aarch64_mops(void* dst, const void* src, std::size_t len);
-void* __folly_memmove_aarch64_simd(void* dst, const void* src, std::size_t len);
-void* __folly_memmove_aarch64_sve(void* dst, const void* src, std::size_t len);
-
-[[gnu::no_sanitize_address]]
-decltype(&__folly_memcpy_aarch64) __folly_detail_memcpy_resolve(
-    uint64_t hwcaps, const void* arg2) {
-#if defined(_IFUNC_ARG_HWCAP)
-  if (hwcaps & _IFUNC_ARG_HWCAP && arg2 != nullptr) {
-    const __ifunc_arg_t* args = reinterpret_cast<const __ifunc_arg_t*>(arg2);
-    if (args->_hwcap2 & HWCAP2_MOPS) {
-      return __folly_memcpy_aarch64_mops;
-    }
-  }
-#endif
-
-  if (hwcaps & HWCAP_SVE) {
-    return __folly_memcpy_aarch64_sve;
-  }
-
-  if (hwcaps & HWCAP_ASIMD) {
-    return __folly_memcpy_aarch64_simd;
-  }
-
-  return __folly_memcpy_aarch64;
-}
-
-[[gnu::no_sanitize_address]]
-decltype(&__folly_memmove_aarch64) __folly_detail_memmove_resolve(
-    uint64_t hwcaps, const void* arg2) {
-#if defined(_IFUNC_ARG_HWCAP)
-  if (hwcaps & _IFUNC_ARG_HWCAP && arg2 != nullptr) {
-    const __ifunc_arg_t* args = reinterpret_cast<const __ifunc_arg_t*>(arg2);
-    if (args->_hwcap2 & HWCAP2_MOPS) {
-      return __folly_memmove_aarch64_mops;
-    }
-  }
-#endif
-
-  if (hwcaps & HWCAP_SVE) {
-    return __folly_memmove_aarch64_sve;
-  }
-
-  if (hwcaps & HWCAP_ASIMD) {
-    return __folly_memmove_aarch64_simd;
-  }
-
-  return __folly_memmove_aarch64;
-}
-
-[[gnu::ifunc("__folly_detail_memcpy_resolve")]]
-void* __folly_memcpy(void* dst, const void* src, std::size_t size);
-
-[[gnu::ifunc("__folly_detail_memmove_resolve")]]
-void* __folly_memmove(void* dst, const void* src, std::size_t size);
-
-#ifdef FOLLY_MEMCPY_IS_MEMCPY
-
-[[gnu::weak, gnu::alias("__folly_memcpy")]]
-void* memcpy(void* dst, const void* src, std::size_t size);
-
-[[gnu::weak, gnu::alias("__folly_memmove")]]
-void* memmove(void* dst, const void* src, std::size_t size);
-
-#endif
-
-} // extern "C"
-
-#endif // defined(__linux__) && defined(__aarch64__)
diff --git a/folly/folly/memory/IoUringArena.cpp b/folly/folly/memory/IoUringArena.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/memory/IoUringArena.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/memory/IoUringArena.h>
+
+#include <folly/CPortability.h>
+#include <folly/Conv.h>
+#include <folly/lang/Align.h>
+#include <folly/memory/Malloc.h>
+#include <folly/portability/String.h>
+#include <folly/portability/SysMman.h>
+#include <folly/portability/Unistd.h>
+
+#include <glog/logging.h>
+
+#if defined(FOLLY_USE_JEMALLOC) && !defined(FOLLY_SANITIZE)
+#define FOLLY_IO_URING_ARENA_SUPPORTED 1
+#else
+#define FOLLY_IO_URING_ARENA_SUPPORTED 0
+#endif // defined(FOLLY_USE_JEMALLOC) && !FOLLY_SANITIZE
+
+#if !FOLLY_IO_URING_ARENA_SUPPORTED
+#undef MALLOCX_ARENA
+#undef MALLOCX_TCACHE_NONE
+#define MALLOCX_ARENA(x) 0
+#define MALLOCX_TCACHE_NONE 0
+
+#if !defined(JEMALLOC_VERSION_MAJOR) || (JEMALLOC_VERSION_MAJOR < 5)
+using extent_hooks_t = struct extent_hooks_s;
+using extent_alloc_t =
+    void*(extent_hooks_t*, void*, size_t, size_t, bool*, bool*, unsigned int);
+struct extent_hooks_s {
+  extent_alloc_t* alloc;
+};
+#endif // JEMALLOC_VERSION_MAJOR
+
+#endif // !FOLLY_IO_URING_ARENA_SUPPORTED
+
+namespace folly {
+namespace {
+
+void printError(int err, const char* msg) {
+  int savedErrno = std::exchange(errno, err);
+  PLOG(ERROR) << msg;
+  errno = savedErrno;
+}
+
+class Arena {
+ public:
+  int init(size_t size);
+
+  void* reserve(size_t size, size_t alignment);
+
+  bool addressInArena(void* address) {
+    auto addr = reinterpret_cast<uintptr_t>(address);
+    return addr >= start_ && addr < end_;
+  }
+
+  void* base() { return reinterpret_cast<void*>(start_); }
+
+  size_t regionSize() { return end_ - start_; }
+
+  size_t freeSpace() { return end_ - freePtr_; }
+
+  unsigned arenaIndex() { return arenaIndex_; }
+
+ private:
+  static void* allocHook(
+      extent_hooks_t* extent,
+      void* new_addr,
+      size_t size,
+      size_t alignment,
+      bool* zero,
+      bool* commit,
+      unsigned arena_ind);
+
+  uintptr_t start_{0};
+  uintptr_t end_{0};
+  uintptr_t freePtr_{0};
+  extent_alloc_t* originalAlloc_{nullptr};
+  extent_hooks_t extentHooks_{};
+  unsigned arenaIndex_{0};
+};
+
+Arena arena;
+
+int Arena::init(size_t size) {
+  const static size_t kPageSize = sysconf(_SC_PAGESIZE);
+  DCHECK(start_ == 0);
+  DCHECK(usingJEMalloc());
+
+  size_t len = sizeof(arenaIndex_);
+  if (auto ret = mallctl("arenas.create", &arenaIndex_, &len, nullptr, 0)) {
+    printError(ret, "Unable to create jemalloc arena");
+    return 0;
+  }
+
+  // Set grow retained limit to stop jemalloc from
+  // forever increasing the requested size after failed allocations.
+  size_t growRetainedLimit = kPageSize;
+  auto rtlKey =
+      folly::to<std::string>("arena.", arenaIndex_, ".retain_grow_limit");
+  if (auto ret = mallctl(
+          rtlKey.c_str(),
+          nullptr,
+          nullptr,
+          &growRetainedLimit,
+          sizeof(growRetainedLimit))) {
+    printError(ret, "Unable to set growth limit");
+    return 0;
+  }
+
+  auto hooksKey =
+      folly::to<std::string>("arena.", arenaIndex_, ".extent_hooks");
+  extent_hooks_t* hooks;
+  len = sizeof(hooks);
+  if (auto ret = mallctl(hooksKey.c_str(), &hooks, &len, nullptr, 0)) {
+    printError(ret, "Unable to get extent hooks");
+    return 0;
+  }
+  originalAlloc_ = hooks->alloc;
+
+  extentHooks_ = *hooks;
+  extentHooks_.alloc = &allocHook;
+  extent_hooks_t* newHooks = &extentHooks_;
+  if (auto ret = mallctl(
+          hooksKey.c_str(), nullptr, nullptr, &newHooks, sizeof(newHooks))) {
+    printError(ret, "Unable to set extent hooks");
+    return 0;
+  }
+
+  ssize_t decayMs = -1;
+  auto dirtyDecayKey =
+      folly::to<std::string>("arena.", arenaIndex_, ".dirty_decay_ms");
+  if (auto ret = mallctl(
+          dirtyDecayKey.c_str(), nullptr, nullptr, &decayMs, sizeof(decayMs))) {
+    printError(ret, "Unable to set dirty decay");
+    return 0;
+  }
+
+  auto muzzyDecayKey =
+      folly::to<std::string>("arena.", arenaIndex_, ".muzzy_decay_ms");
+  if (auto ret = mallctl(
+          muzzyDecayKey.c_str(), nullptr, nullptr, &decayMs, sizeof(decayMs))) {
+    printError(ret, "Unable to set muzzy decay");
+    return 0;
+  }
+
+  void* ptr = mmap(
+      nullptr,
+      size,
+      PROT_READ | PROT_WRITE,
+      MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE,
+      -1,
+      0);
+  if (ptr == MAP_FAILED) {
+    printError(errno, "mmap failed for IoUringArena");
+    return 0;
+  }
+
+  start_ = freePtr_ = reinterpret_cast<uintptr_t>(ptr);
+  end_ = start_ + size;
+
+  return MALLOCX_ARENA(arenaIndex_) | MALLOCX_TCACHE_NONE;
+}
+
+void* Arena::reserve(size_t size, size_t alignment) {
+  uintptr_t res = folly::align_ceil(freePtr_, alignment);
+  uintptr_t newFreePtr = res + size;
+  if (newFreePtr > end_) {
+    return nullptr;
+  }
+  freePtr_ = newFreePtr;
+  return reinterpret_cast<void*>(res);
+}
+
+void* Arena::allocHook(
+    extent_hooks_t* extent,
+    void* new_addr,
+    size_t size,
+    size_t alignment,
+    bool* zero,
+    bool* commit,
+    unsigned arena_ind) {
+  void* res = nullptr;
+  if (new_addr == nullptr) {
+    res = arena.reserve(size, alignment);
+  }
+  if (res == nullptr) {
+    res = arena.originalAlloc_(
+        extent, new_addr, size, alignment, zero, commit, arena_ind);
+  } else {
+    if (*zero) {
+      memset(res, 0, size);
+    }
+    *commit = true;
+  }
+  return res;
+}
+
+} // namespace
+
+int IoUringArena::flags_{0};
+
+bool IoUringArena::init(size_t size) {
+  if (ioUringArenaSupported()) {
+    if (flags_ == 0) {
+      flags_ = arena.init(size);
+    }
+  } else {
+    LOG(WARNING) << "IoUringArena not supported";
+  }
+  return flags_ != 0;
+}
+
+void* IoUringArena::allocate(size_t size) {
+  return ioUringArenaSupported() ? mallocx(size, flags_) : malloc(size);
+}
+
+void* IoUringArena::reallocate(void* p, size_t size) {
+  return ioUringArenaSupported() ? rallocx(p, size, flags_) : realloc(p, size);
+}
+
+void IoUringArena::deallocate(void* p, size_t) {
+  ioUringArenaSupported() ? dallocx(p, flags_) : free(p);
+}
+
+bool IoUringArena::initialized() {
+  return flags_ != 0;
+}
+
+bool IoUringArena::addressInArena(void* address) {
+  return arena.addressInArena(address);
+}
+
+void* IoUringArena::base() {
+  return arena.base();
+}
+
+size_t IoUringArena::regionSize() {
+  return arena.regionSize();
+}
+
+size_t IoUringArena::freeSpace() {
+  return arena.freeSpace();
+}
+
+unsigned IoUringArena::arenaIndex() {
+  return arena.arenaIndex();
+}
+
+int IoUringArena::flags() {
+  return flags_;
+}
+
+bool IoUringArena::ioUringArenaSupported() {
+  static const bool kIoUringArenaSupported{
+      FOLLY_IO_URING_ARENA_SUPPORTED && folly::usingJEMalloc()};
+  return kIoUringArenaSupported;
+}
+
+} // namespace folly
diff --git a/folly/folly/memory/IoUringArena.h b/folly/folly/memory/IoUringArena.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/memory/IoUringArena.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+namespace folly {
+
+class IoUringArena {
+ public:
+  static bool init(size_t size);
+
+  static void* allocate(size_t size);
+
+  static void* reallocate(void* p, size_t size);
+
+  static void deallocate(void* p, size_t size = 0);
+
+  static bool initialized();
+
+  static bool addressInArena(void* address);
+
+  static void* base();
+
+  static size_t regionSize();
+
+  static size_t freeSpace();
+
+  static unsigned arenaIndex();
+
+  static int flags();
+
+  static bool ioUringArenaSupported();
+
+ private:
+  static int flags_;
+};
+
+template <typename T>
+class CxxIoUringAllocator {
+ public:
+  using value_type = T;
+
+  CxxIoUringAllocator() = default;
+
+  template <typename U>
+  explicit CxxIoUringAllocator(CxxIoUringAllocator<U> const&) {}
+
+  T* allocate(std::size_t n) {
+    return static_cast<T*>(IoUringArena::allocate(sizeof(T) * n));
+  }
+
+  void deallocate(T* p, std::size_t n) {
+    IoUringArena::deallocate(p, sizeof(T) * n);
+  }
+
+  friend bool operator==(
+      CxxIoUringAllocator const&, CxxIoUringAllocator const&) noexcept {
+    return true;
+  }
+
+  friend bool operator!=(
+      CxxIoUringAllocator const&, CxxIoUringAllocator const&) noexcept {
+    return false;
+  }
+};
+
+} // namespace folly
diff --git a/folly/folly/memory/JemallocNodumpAllocator.cpp b/folly/folly/memory/JemallocNodumpAllocator.cpp
--- a/folly/folly/memory/JemallocNodumpAllocator.cpp
+++ b/folly/folly/memory/JemallocNodumpAllocator.cpp
@@ -18,7 +18,6 @@
 
 #include <folly/Conv.h>
 #include <folly/String.h>
-#include <folly/memory/Malloc.h>
 
 #include <glog/logging.h>
 
diff --git a/folly/folly/memory/Malloc.cpp b/folly/folly/memory/Malloc.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/memory/Malloc.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/memory/Malloc.h>
+
+namespace folly {
+
+namespace detail {
+
+namespace {
+
+struct DoUsingJEMallocInitializer {
+  bool operator()() const noexcept {
+    // Checking for rallocx != nullptr is not sufficient; we may be in a
+    // dlopen()ed module that depends on libjemalloc, so rallocx is resolved,
+    // but the main program might be using a different memory allocator. How
+    // do we determine that we're using jemalloc? In the hackiest way
+    // possible. We allocate memory using malloc() and see if the per-thread
+    // counter of allocated memory increases. This makes me feel dirty inside.
+    // Also note that this requires jemalloc to have been compiled with
+    // --enable-stats.
+
+    // Some platforms (*cough* OSX *cough*) require weak symbol checks to be
+    // in the form if (mallctl != nullptr). Not if (mallctl) or if (!mallctl)
+    // (!!). http://goo.gl/xpmctm
+    if (mallocx == nullptr || rallocx == nullptr || xallocx == nullptr ||
+        sallocx == nullptr || dallocx == nullptr || sdallocx == nullptr ||
+        nallocx == nullptr || mallctl == nullptr ||
+        mallctlnametomib == nullptr || mallctlbymib == nullptr) {
+      return false;
+    }
+
+    // "volatile" because gcc optimizes out the reads from *counter, because
+    // it "knows" malloc doesn't modify global state...
+    /* nolint */ volatile uint64_t* counter;
+    size_t counterLen = sizeof(uint64_t*);
+
+    if (mallctl(
+            "thread.allocatedp",
+            static_cast<void*>(&counter),
+            &counterLen,
+            nullptr,
+            0) != 0) {
+      return false;
+    }
+
+    if (counterLen != sizeof(uint64_t*)) {
+      return false;
+    }
+
+    uint64_t origAllocated = *counter;
+
+    static void* volatile ptr = malloc(1);
+    if (!ptr) {
+      // wtf, failing to allocate 1 byte
+      return false;
+    }
+
+    free(ptr);
+
+    return (origAllocated != *counter);
+  }
+};
+
+} // namespace
+
+bool UsingJEMallocInitializer::operator()() const noexcept {
+  // Absolutely wild, but this apparently needs to be cached within this TU.
+  using Initializer = DoUsingJEMallocInitializer;
+  return detail::FastStaticBool<Initializer>::get(std::memory_order_relaxed);
+}
+
+namespace {
+
+struct DoUsingTCMallocInitializer {
+  bool operator()() const noexcept {
+    // See comment in usingJEMalloc().
+    if (MallocExtension_Internal_GetNumericProperty == nullptr ||
+        sdallocx == nullptr || nallocx == nullptr) {
+      return false;
+    }
+    static const char kAllocBytes[] = "generic.current_allocated_bytes";
+
+    size_t before_bytes = 0;
+    getTCMallocNumericProperty(kAllocBytes, &before_bytes);
+
+    static void* volatile ptr = malloc(1);
+    if (!ptr) {
+      // wtf, failing to allocate 1 byte
+      return false;
+    }
+
+    size_t after_bytes = 0;
+    getTCMallocNumericProperty(kAllocBytes, &after_bytes);
+
+    free(ptr);
+
+    return (before_bytes != after_bytes);
+  }
+};
+
+} // namespace
+
+bool UsingTCMallocInitializer::operator()() const noexcept {
+  // Absolutely wild, but this apparently needs to be cached within this TU.
+  using Initializer = DoUsingTCMallocInitializer;
+  return detail::FastStaticBool<Initializer>::get(std::memory_order_relaxed);
+}
+
+} // namespace detail
+
+} // namespace folly
diff --git a/folly/folly/memory/Malloc.h b/folly/folly/memory/Malloc.h
--- a/folly/folly/memory/Malloc.h
+++ b/folly/folly/memory/Malloc.h
@@ -111,6 +111,14 @@
 };
 #endif
 
+struct UsingJEMallocInitializer {
+  bool operator()() const noexcept;
+};
+
+struct UsingTCMallocInitializer {
+  bool operator()() const noexcept;
+};
+
 } // namespace detail
 
 #if defined(__GNUC__)
@@ -142,58 +150,7 @@
 #else
 #define FOLLY_CONSTANT_USING_JE_MALLOC 0
 FOLLY_EXPORT inline bool usingJEMalloc() noexcept {
-  struct Initializer {
-    bool operator()() const {
-      // Checking for rallocx != nullptr is not sufficient; we may be in a
-      // dlopen()ed module that depends on libjemalloc, so rallocx is resolved,
-      // but the main program might be using a different memory allocator. How
-      // do we determine that we're using jemalloc? In the hackiest way
-      // possible. We allocate memory using malloc() and see if the per-thread
-      // counter of allocated memory increases. This makes me feel dirty inside.
-      // Also note that this requires jemalloc to have been compiled with
-      // --enable-stats.
-
-      // Some platforms (*cough* OSX *cough*) require weak symbol checks to be
-      // in the form if (mallctl != nullptr). Not if (mallctl) or if (!mallctl)
-      // (!!). http://goo.gl/xpmctm
-      if (mallocx == nullptr || rallocx == nullptr || xallocx == nullptr ||
-          sallocx == nullptr || dallocx == nullptr || sdallocx == nullptr ||
-          nallocx == nullptr || mallctl == nullptr ||
-          mallctlnametomib == nullptr || mallctlbymib == nullptr) {
-        return false;
-      }
-
-      // "volatile" because gcc optimizes out the reads from *counter, because
-      // it "knows" malloc doesn't modify global state...
-      /* nolint */ volatile uint64_t* counter;
-      size_t counterLen = sizeof(uint64_t*);
-
-      if (mallctl(
-              "thread.allocatedp",
-              static_cast<void*>(&counter),
-              &counterLen,
-              nullptr,
-              0) != 0) {
-        return false;
-      }
-
-      if (counterLen != sizeof(uint64_t*)) {
-        return false;
-      }
-
-      uint64_t origAllocated = *counter;
-
-      static void* volatile ptr = malloc(1);
-      if (!ptr) {
-        // wtf, failing to allocate 1 byte
-        return false;
-      }
-
-      free(ptr);
-
-      return (origAllocated != *counter);
-    }
-  };
+  using Initializer = detail::UsingJEMallocInitializer;
   return detail::FastStaticBool<Initializer>::get(std::memory_order_relaxed);
 }
 #endif
@@ -230,32 +187,7 @@
 #else
 #define FOLLY_CONSTANT_USING_TC_MALLOC 0
 FOLLY_EXPORT inline bool usingTCMalloc() noexcept {
-  struct Initializer {
-    bool operator()() const {
-      // See comment in usingJEMalloc().
-      if (MallocExtension_Internal_GetNumericProperty == nullptr ||
-          sdallocx == nullptr || nallocx == nullptr) {
-        return false;
-      }
-      static const char kAllocBytes[] = "generic.current_allocated_bytes";
-
-      size_t before_bytes = 0;
-      getTCMallocNumericProperty(kAllocBytes, &before_bytes);
-
-      static void* volatile ptr = malloc(1);
-      if (!ptr) {
-        // wtf, failing to allocate 1 byte
-        return false;
-      }
-
-      size_t after_bytes = 0;
-      getTCMallocNumericProperty(kAllocBytes, &after_bytes);
-
-      free(ptr);
-
-      return (before_bytes != after_bytes);
-    }
-  };
+  using Initializer = detail::UsingTCMallocInitializer;
   return detail::FastStaticBool<Initializer>::get(std::memory_order_relaxed);
 }
 #endif
diff --git a/folly/folly/memory/ReentrantAllocator.h b/folly/folly/memory/ReentrantAllocator.h
--- a/folly/folly/memory/ReentrantAllocator.h
+++ b/folly/folly/memory/ReentrantAllocator.h
@@ -205,7 +205,7 @@
 
   //  allocate
   template <typename..., typename S = T, if_is_not_void<S> = 0>
-  FOLLY_NODISCARD FOLLY_ERASE T* allocate(std::size_t n) {
+  [[nodiscard]] FOLLY_ERASE T* allocate(std::size_t n) {
     return static_cast<T*>(base::allocate(n * sizeof(T), alignof(T)));
   }
 
diff --git a/folly/folly/memory/UninitializedMemoryHacks.h b/folly/folly/memory/UninitializedMemoryHacks.h
--- a/folly/folly/memory/UninitializedMemoryHacks.h
+++ b/folly/folly/memory/UninitializedMemoryHacks.h
@@ -310,7 +310,10 @@
   pointer __begin_;
   pointer __end_;
 #ifdef _LIBCPP_COMPRESSED_PAIR
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wgnu-anonymous-struct"
   _LIBCPP_COMPRESSED_PAIR(pointer, __cap_ = nullptr, allocator_type, __alloc_);
+#pragma GCC diagnostic pop
 #else
   std::__compressed_pair<pointer, allocator_type> __end_cap_;
 #endif
diff --git a/folly/folly/memory/not_null-inl.h b/folly/folly/memory/not_null-inl.h
--- a/folly/folly/memory/not_null-inl.h
+++ b/folly/folly/memory/not_null-inl.h
@@ -35,12 +35,12 @@
 auto maybeUnwrap(T&& t) {
   return std::forward<T>(t);
 }
-template <typename T>
-auto maybeUnwrap(const not_null_base<T>& t) {
+template <typename T, typename NullHandlerT>
+auto maybeUnwrap(const not_null_base<T, NullHandlerT>& t) {
   return t.unwrap();
 }
-template <typename T>
-auto maybeUnwrap(not_null_base<T>&& t) {
+template <typename T, typename NullHandlerT>
+auto maybeUnwrap(not_null_base<T, NullHandlerT>&& t) {
   return std::move(t).unwrap();
 }
 
@@ -60,8 +60,8 @@
 struct maybe_unwrap_not_null<T&&> {
   using type = typename maybe_unwrap_not_null<T>::type&&;
 };
-template <typename PtrT>
-struct maybe_unwrap_not_null<not_null<PtrT>> {
+template <typename PtrT, typename NullHandlerT>
+struct maybe_unwrap_not_null<not_null<PtrT, NullHandlerT>> {
   using type = PtrT;
 };
 
@@ -88,18 +88,33 @@
 //  - It must not already be castable, otherwise the compiler will raise an
 //    ambiguity error
 //  - PtrT must be castable to ToT
+//
+// NOTE: We use std::conjunction for short-circuit evaluation to avoid infinite
+// template recursion. With std::integral_constant<bool, A && B>, both operands
+// are instantiated even when A is false. When external code checks
+// std::is_convertible<not_null<PtrT>, U>, this instantiates operator U(),
+// whose SFINAE constraint instantiates is_not_null_castable, which would check
+// std::is_convertible<not_null<PtrT>, U> again, causing unbounded recursion.
+// std::conjunction short-circuits: if the first trait is false_type, the second
+// trait is not instantiated, breaking the cycle.
 template <typename FromPtrT, typename ToT>
 struct is_not_null_castable
-    : std::integral_constant<
-          bool,
-          std::is_convertible_v<const FromPtrT&, ToT> &&
-              !std::is_convertible_v<const not_null<FromPtrT>&, ToT>> {};
+    : std::conjunction<
+          std::is_convertible<const FromPtrT&, ToT>,
+          std::negation<std::is_convertible<
+              // No need to specialize based on null handler as it doesn't
+              // affect the result.
+              const not_null<FromPtrT, default_null_handler>&,
+              ToT>>> {};
 template <typename FromPtrT, typename ToT>
 struct is_not_null_move_castable
-    : std::integral_constant<
-          bool,
-          std::is_convertible_v<FromPtrT&&, ToT> &&
-              !std::is_convertible_v<not_null<FromPtrT>&&, ToT>> {};
+    : std::conjunction<
+          std::is_convertible<FromPtrT&&, ToT>,
+          std::negation<std::is_convertible<
+              // No need to specialize based on null handler as it doesn't
+              // affect the result.
+              not_null<FromPtrT, default_null_handler>&&,
+              ToT>>> {};
 
 template <typename T, typename = decltype(*std::declval<T*>() == nullptr)>
 inline std::true_type is_comparable_to_nullptr_fn(const T&) {
@@ -113,56 +128,68 @@
     decltype(is_comparable_to_nullptr_fn(*std::declval<T*>()))::value;
 } // namespace detail
 
-template <typename PtrT>
+[[noreturn]] /*static*/ inline void default_null_handler::handle_terminate(
+    const char* const msg) {
+  folly::terminate_with<std::runtime_error>(msg);
+}
+
+[[noreturn]] /*static*/ inline void default_null_handler::handle_throw(
+    const char* const msg) {
+  folly::throw_exception<std::invalid_argument>(msg);
+}
+
+template <typename PtrT, typename NullHandlerT>
 template <typename U>
-not_null_base<PtrT>::not_null_base(U&& u, private_tag)
+not_null_base<PtrT, NullHandlerT>::not_null_base(U&& u, private_tag)
     : ptr_(detail::maybeUnwrap(std::forward<U>(u))) {
   if constexpr (!detail::is_not_null_v<remove_cvref_t<U>>) {
     throw_if_null();
   }
 }
 
-template <typename PtrT>
-not_null_base<PtrT>::not_null_base(
+template <typename PtrT, typename NullHandlerT>
+not_null_base<PtrT, NullHandlerT>::not_null_base(
     PtrT&& ptr, guaranteed_not_null_provider::guaranteed_not_null) noexcept
     : ptr_(std::move(ptr)) {}
 
-template <typename PtrT>
+template <typename PtrT, typename NullHandlerT>
 template <typename U, typename>
-not_null_base<PtrT>::not_null_base(U&& u, implicit_tag<true>) noexcept(
-    detail::is_not_null_nothrow_constructible<U&&, PtrT>::value)
+not_null_base<PtrT, NullHandlerT>::
+    not_null_base(U&& u, implicit_tag<true>) noexcept(
+        detail::is_not_null_nothrow_constructible<U&&, PtrT>::value)
     : not_null_base(std::forward<U>(u), private_tag{}) {}
 
-template <typename PtrT>
+template <typename PtrT, typename NullHandlerT>
 template <typename U, typename>
-not_null_base<PtrT>::not_null_base(U&& u, implicit_tag<false>) noexcept(
-    detail::is_not_null_nothrow_constructible<U&&, PtrT>::value)
+not_null_base<PtrT, NullHandlerT>::
+    not_null_base(U&& u, implicit_tag<false>) noexcept(
+        detail::is_not_null_nothrow_constructible<U&&, PtrT>::value)
     : not_null_base(std::forward<U>(u), private_tag{}) {}
 
-template <typename PtrT>
-typename not_null_base<PtrT>::element_type& not_null_base<PtrT>::operator*()
-    const noexcept {
+template <typename PtrT, typename NullHandlerT>
+typename not_null_base<PtrT, NullHandlerT>::element_type&
+not_null_base<PtrT, NullHandlerT>::operator*() const noexcept {
   return *unwrap();
 }
 
-template <typename PtrT>
-const PtrT& not_null_base<PtrT>::operator->() const noexcept {
+template <typename PtrT, typename NullHandlerT>
+const PtrT& not_null_base<PtrT, NullHandlerT>::operator->() const noexcept {
   return unwrap();
 }
 
-template <typename PtrT>
-not_null_base<PtrT>::operator const PtrT&() const& noexcept {
+template <typename PtrT, typename NullHandlerT>
+not_null_base<PtrT, NullHandlerT>::operator const PtrT&() const& noexcept {
   return unwrap();
 }
 
-template <typename PtrT>
-not_null_base<PtrT>::operator PtrT&&() && noexcept {
+template <typename PtrT, typename NullHandlerT>
+not_null_base<PtrT, NullHandlerT>::operator PtrT&&() && noexcept {
   return std::move(*this).unwrap();
 }
 
-template <typename PtrT>
+template <typename PtrT, typename NullHandlerT>
 template <typename U, typename>
-not_null_base<PtrT>::operator U() const& noexcept(
+not_null_base<PtrT, NullHandlerT>::operator U() const& noexcept(
     std::is_nothrow_constructible_v<U, const PtrT&>) {
   if constexpr (detail::is_not_null_v<U>) {
     return U(*this);
@@ -170,9 +197,9 @@
   return U(unwrap());
 }
 
-template <typename PtrT>
+template <typename PtrT, typename NullHandlerT>
 template <typename U, typename>
-not_null_base<PtrT>::operator U() && noexcept(
+not_null_base<PtrT, NullHandlerT>::operator U() && noexcept(
     std::is_nothrow_constructible_v<U, PtrT&&>) {
   if constexpr (detail::is_not_null_v<U>) {
     return U(std::move(*this));
@@ -180,61 +207,60 @@
   return U(std::move(*this).unwrap());
 }
 
-template <typename PtrT>
-void not_null_base<PtrT>::swap(not_null_base& other) noexcept {
+template <typename PtrT, typename NullHandlerT>
+void not_null_base<PtrT, NullHandlerT>::swap(not_null_base& other) noexcept {
   mutable_unwrap().swap(other.mutable_unwrap());
 }
 
-template <typename PtrT>
-const PtrT& not_null_base<PtrT>::unwrap() const& noexcept {
+template <typename PtrT, typename NullHandlerT>
+const PtrT& not_null_base<PtrT, NullHandlerT>::unwrap() const& noexcept {
   if constexpr (folly::kIsDebug) {
     terminate_if_null(ptr_);
   }
   return ptr_;
 }
 
-template <typename PtrT>
-PtrT&& not_null_base<PtrT>::unwrap() && noexcept {
+template <typename PtrT, typename NullHandlerT>
+PtrT&& not_null_base<PtrT, NullHandlerT>::unwrap() && noexcept {
   if constexpr (folly::kIsDebug) {
     terminate_if_null(ptr_);
   }
   return std::move(ptr_);
 }
 
-template <typename PtrT>
-PtrT& not_null_base<PtrT>::mutable_unwrap() noexcept {
+template <typename PtrT, typename NullHandlerT>
+PtrT& not_null_base<PtrT, NullHandlerT>::mutable_unwrap() noexcept {
   return const_cast<PtrT&>(const_cast<const not_null_base&>(*this).unwrap());
 }
 
-template <typename PtrT>
-void not_null_base<PtrT>::throw_if_null() const {
+template <typename PtrT, typename NullHandlerT>
+void not_null_base<PtrT, NullHandlerT>::throw_if_null() const {
   throw_if_null(ptr_);
 }
 
-template <typename PtrT>
+template <typename PtrT, typename NullHandlerT>
 template <typename T>
-void not_null_base<PtrT>::throw_if_null(const T& ptr) {
+void not_null_base<PtrT, NullHandlerT>::throw_if_null(const T& ptr) {
   if (ptr == nullptr) {
-    folly::throw_exception<std::invalid_argument>("non_null<PtrT> is null");
+    NullHandlerT::handle_throw("non_null<PtrT> is null");
   }
 }
 
-template <typename PtrT>
+template <typename PtrT, typename NullHandlerT>
 template <typename T>
-void not_null_base<PtrT>::terminate_if_null(const T& ptr) {
+void not_null_base<PtrT, NullHandlerT>::terminate_if_null(const T& ptr) {
   if (ptr == nullptr) {
-    folly::terminate_with<std::runtime_error>(
-        "not_null internal pointer is null");
+    NullHandlerT::handle_terminate("not_null internal pointer is null");
   }
 }
 
-template <typename PtrT>
+template <typename PtrT, typename NullHandlerT>
 template <typename Deleter>
-Deleter&& not_null_base<PtrT>::forward_or_throw_if_null(Deleter&& deleter) {
+Deleter&& not_null_base<PtrT, NullHandlerT>::forward_or_throw_if_null(
+    Deleter&& deleter) {
   if constexpr (detail::is_comparable_to_nullptr_v<Deleter>) {
     if (deleter == nullptr) {
-      folly::throw_exception<std::invalid_argument>(
-          "non_null<PtrT> deleter is null");
+      NullHandlerT::handle_throw("non_null<PtrT> deleter is null");
     }
   }
   return std::forward<Deleter>(deleter);
@@ -243,41 +269,46 @@
 /**
  * not_null<std::unique_ptr<>> specialization.
  */
-template <typename T, typename Deleter>
-not_null<std::unique_ptr<T, Deleter>>::not_null(pointer p, const Deleter& d)
-    : not_null_base<std::unique_ptr<T, Deleter>>(
+template <typename T, typename Deleter, typename NullHandlerT>
+not_null<std::unique_ptr<T, Deleter>, NullHandlerT>::not_null(
+    pointer p, const Deleter& d)
+    : not_null_base<std::unique_ptr<T, Deleter>, NullHandlerT>(
           std::unique_ptr<T, Deleter>(
               std::move(p).unwrap(), this->forward_or_throw_if_null(d)),
           guaranteed_not_null_provider::guaranteed_not_null()) {}
 
-template <typename T, typename Deleter>
-not_null<std::unique_ptr<T, Deleter>>::not_null(pointer p, Deleter&& d)
-    : not_null_base<std::unique_ptr<T, Deleter>>(
+template <typename T, typename Deleter, typename NullHandlerT>
+not_null<std::unique_ptr<T, Deleter>, NullHandlerT>::not_null(
+    pointer p, Deleter&& d)
+    : not_null_base<std::unique_ptr<T, Deleter>, NullHandlerT>(
           std::unique_ptr<T, Deleter>(
               std::move(p).unwrap(),
               this->forward_or_throw_if_null(std::move(d))),
           guaranteed_not_null_provider::guaranteed_not_null()) {}
 
-template <typename T, typename Deleter>
-void not_null<std::unique_ptr<T, Deleter>>::reset(pointer ptr) noexcept {
+template <typename T, typename Deleter, typename NullHandlerT>
+void not_null<std::unique_ptr<T, Deleter>, NullHandlerT>::reset(
+    pointer ptr) noexcept {
   this->mutable_unwrap().reset(ptr.unwrap());
 }
 
-template <typename T, typename Deleter>
-typename not_null<std::unique_ptr<T, Deleter>>::pointer
-not_null<std::unique_ptr<T, Deleter>>::get() const noexcept {
+template <typename T, typename Deleter, typename NullHandlerT>
+typename not_null<std::unique_ptr<T, Deleter>, NullHandlerT>::pointer
+not_null<std::unique_ptr<T, Deleter>, NullHandlerT>::get() const noexcept {
   return pointer(
       this->unwrap().get(),
       guaranteed_not_null_provider::guaranteed_not_null());
 }
 
-template <typename T, typename Deleter>
-Deleter& not_null<std::unique_ptr<T, Deleter>>::get_deleter() noexcept {
+template <typename T, typename Deleter, typename NullHandlerT>
+Deleter&
+not_null<std::unique_ptr<T, Deleter>, NullHandlerT>::get_deleter() noexcept {
   return this->mutable_unwrap().get_deleter();
 }
 
-template <typename T, typename Deleter>
-const Deleter& not_null<std::unique_ptr<T, Deleter>>::get_deleter()
+template <typename T, typename Deleter, typename NullHandlerT>
+const Deleter&
+not_null<std::unique_ptr<T, Deleter>, NullHandlerT>::get_deleter()
     const noexcept {
   return this->unwrap().get_deleter();
 }
@@ -292,104 +323,110 @@
 /**
  * not_null<std::shared_ptr<>> specialization.
  */
-template <typename T>
+template <typename T, typename NullHandlerT>
 template <typename U, typename Deleter>
-not_null<std::shared_ptr<T>>::not_null(U* ptr, Deleter d)
-    : not_null_base<std::shared_ptr<T>>(std::shared_ptr<T>(
+not_null<std::shared_ptr<T>, NullHandlerT>::not_null(U* ptr, Deleter d)
+    : not_null_base<std::shared_ptr<T>, NullHandlerT>(std::shared_ptr<T>(
           ptr, this->forward_or_throw_if_null(std::move(d)))) {}
 
-template <typename T>
-template <typename U, typename Deleter>
-not_null<std::shared_ptr<T>>::not_null(not_null<U*> ptr, Deleter d)
-    : not_null_base<std::shared_ptr<T>>(
+template <typename T, typename NullHandlerT>
+template <typename U, typename Deleter, typename UNullHandlerT>
+not_null<std::shared_ptr<T>, NullHandlerT>::not_null(
+    not_null<U*, UNullHandlerT> ptr, Deleter d)
+    : not_null_base<std::shared_ptr<T>, NullHandlerT>(
           std::shared_ptr<T>(
               ptr.unwrap(), this->forward_or_throw_if_null(std::move(d))),
           guaranteed_not_null_provider::guaranteed_not_null()) {}
 
-template <typename T>
+template <typename T, typename NullHandlerT>
 template <typename U>
-not_null<std::shared_ptr<T>>::not_null(
-    const std::shared_ptr<U>& r, not_null<element_type*> ptr) noexcept
-    : not_null_base<std::shared_ptr<T>>(
+not_null<std::shared_ptr<T>, NullHandlerT>::not_null(
+    const std::shared_ptr<U>& r,
+    not_null<element_type*, NullHandlerT> ptr) noexcept
+    : not_null_base<std::shared_ptr<T>, NullHandlerT>(
           std::shared_ptr<T>(r, ptr.unwrap()),
           guaranteed_not_null_provider::guaranteed_not_null()) {}
 
-template <typename T>
-template <typename U>
-not_null<std::shared_ptr<T>>::not_null(
-    const not_null<std::shared_ptr<U>>& r, not_null<element_type*> ptr) noexcept
-    : not_null_base<std::shared_ptr<T>>(
+template <typename T, typename NullHandlerT>
+template <typename U, typename UNullHandlerT>
+not_null<std::shared_ptr<T>, NullHandlerT>::not_null(
+    const not_null<std::shared_ptr<U>, UNullHandlerT>& r,
+    not_null<element_type*, NullHandlerT> ptr) noexcept
+    : not_null_base<std::shared_ptr<T>, NullHandlerT>(
           std::shared_ptr<T>(r.unwrap(), ptr.unwrap()),
           guaranteed_not_null_provider::guaranteed_not_null()) {}
 
-template <typename T>
+template <typename T, typename NullHandlerT>
 template <typename U>
-not_null<std::shared_ptr<T>>::not_null(
-    std::shared_ptr<U>&& r, not_null<element_type*> ptr) noexcept
-    : not_null_base<std::shared_ptr<T>>(
+not_null<std::shared_ptr<T>, NullHandlerT>::not_null(
+    std::shared_ptr<U>&& r, not_null<element_type*, NullHandlerT> ptr) noexcept
+    : not_null_base<std::shared_ptr<T>, NullHandlerT>(
           std::shared_ptr<T>(std::move(r), ptr.unwrap()),
           guaranteed_not_null_provider::guaranteed_not_null()) {}
 
-template <typename T>
-template <typename U>
-not_null<std::shared_ptr<T>>::not_null(
-    not_null<std::shared_ptr<U>>&& r, not_null<element_type*> ptr) noexcept
-    : not_null_base<std::shared_ptr<T>>(
+template <typename T, typename NullHandlerT>
+template <typename U, typename UNullHandlerT>
+not_null<std::shared_ptr<T>, NullHandlerT>::not_null(
+    not_null<std::shared_ptr<U>, UNullHandlerT>&& r,
+    not_null<element_type*, NullHandlerT> ptr) noexcept
+    : not_null_base<std::shared_ptr<T>, NullHandlerT>(
           std::shared_ptr<T>(std::move(r).unwrap(), ptr.unwrap()),
           guaranteed_not_null_provider::guaranteed_not_null()) {}
 
-template <typename T>
+template <typename T, typename NullHandlerT>
 template <typename U>
-void not_null<std::shared_ptr<T>>::reset(U* ptr) {
+void not_null<std::shared_ptr<T>, NullHandlerT>::reset(U* ptr) {
   this->throw_if_null(ptr);
   this->mutable_unwrap().reset(ptr);
 }
 
-template <typename T>
-template <typename U>
-void not_null<std::shared_ptr<T>>::reset(not_null<U*> ptr) noexcept {
+template <typename T, typename NullHandlerT>
+template <typename U, typename UNullHandlerT>
+void not_null<std::shared_ptr<T>, NullHandlerT>::reset(
+    not_null<U*, UNullHandlerT> ptr) noexcept {
   this->mutable_unwrap().reset(ptr.unwrap());
 }
 
-template <typename T>
+template <typename T, typename NullHandlerT>
 template <typename U, typename Deleter>
-void not_null<std::shared_ptr<T>>::reset(U* ptr, Deleter d) {
+void not_null<std::shared_ptr<T>, NullHandlerT>::reset(U* ptr, Deleter d) {
   this->throw_if_null(ptr);
   this->mutable_unwrap().reset(
       ptr, this->forward_or_throw_if_null(std::move(d)));
 }
 
-template <typename T>
-template <typename U, typename Deleter>
-void not_null<std::shared_ptr<T>>::reset(not_null<U*> ptr, Deleter d) {
+template <typename T, typename NullHandlerT>
+template <typename U, typename Deleter, typename UNullHandlerT>
+void not_null<std::shared_ptr<T>, NullHandlerT>::reset(
+    not_null<U*, UNullHandlerT> ptr, Deleter d) {
   this->mutable_unwrap().reset(
       ptr.unwrap(), this->forward_or_throw_if_null(std::move(d)));
 }
 
-template <typename T>
-typename not_null<std::shared_ptr<T>>::pointer
-not_null<std::shared_ptr<T>>::get() const noexcept {
+template <typename T, typename NullHandlerT>
+typename not_null<std::shared_ptr<T>, NullHandlerT>::pointer
+not_null<std::shared_ptr<T>, NullHandlerT>::get() const noexcept {
   return pointer(
       this->unwrap().get(),
       guaranteed_not_null_provider::guaranteed_not_null());
 }
 
-template <typename T>
-long not_null<std::shared_ptr<T>>::use_count() const noexcept {
+template <typename T, typename NullHandlerT>
+long not_null<std::shared_ptr<T>, NullHandlerT>::use_count() const noexcept {
   return this->unwrap().use_count();
 }
 
-template <typename T>
+template <typename T, typename NullHandlerT>
 template <typename U>
-bool not_null<std::shared_ptr<T>>::owner_before(
+bool not_null<std::shared_ptr<T>, NullHandlerT>::owner_before(
     const std::shared_ptr<U>& other) const noexcept {
   return this->unwrap().owner_before(other);
 }
 
-template <typename T>
-template <typename U>
-bool not_null<std::shared_ptr<T>>::owner_before(
-    const not_null<std::shared_ptr<U>>& other) const noexcept {
+template <typename T, typename NullHandlerT>
+template <typename U, typename UNullHandlerT>
+bool not_null<std::shared_ptr<T>, NullHandlerT>::owner_before(
+    const not_null<std::shared_ptr<U>, UNullHandlerT>& other) const noexcept {
   return this->unwrap().owner_before(other.unwrap());
 }
 
@@ -404,22 +441,21 @@
 not_null_shared_ptr<T> allocate_not_null_shared(
     const Alloc& alloc, Args&&... args) {
   return not_null_shared_ptr<T>(
-      std::allocate_shared<T, Alloc, Args...>(
-          alloc, std::forward<Args>(args)...),
+      std::allocate_shared<T, Alloc>(alloc, std::forward<Args>(args)...),
       detail::secret_guaranteed_not_null::get());
 }
 
 /**
  * Comparators.
  */
-#define FB_NOT_NULL_MK_OP(op)                                 \
-  template <typename PtrT, typename T>                        \
-  bool operator op(const not_null<PtrT>& lhs, const T& rhs) { \
-    return lhs.unwrap() op rhs;                               \
-  }                                                           \
-  template <typename PtrT, typename T, typename>              \
-  bool operator op(const T& lhs, const not_null<PtrT>& rhs) { \
-    return lhs op rhs.unwrap();                               \
+#define FB_NOT_NULL_MK_OP(op)                                                  \
+  template <typename PtrT, typename T, typename LhsNullHandlerT>               \
+  bool operator op(const not_null<PtrT, LhsNullHandlerT>& lhs, const T& rhs) { \
+    return lhs.unwrap() op rhs;                                                \
+  }                                                                            \
+  template <typename PtrT, typename T, typename RhsNullHandlerT, typename>     \
+  bool operator op(const T& lhs, const not_null<PtrT, RhsNullHandlerT>& rhs) { \
+    return lhs op rhs.unwrap();                                                \
   }
 
 FB_NOT_NULL_MK_OP(==)
@@ -433,9 +469,9 @@
 /**
  * Output.
  */
-template <typename U, typename V, typename PtrT>
+template <typename U, typename V, typename PtrT, typename NullHandlerT>
 std::basic_ostream<U, V>& operator<<(
-    std::basic_ostream<U, V>& os, const not_null<PtrT>& ptr) {
+    std::basic_ostream<U, V>& os, const not_null<PtrT, NullHandlerT>& ptr) {
   return os << ptr.unwrap();
 }
 
@@ -450,57 +486,88 @@
 /**
  * Getters
  */
-template <typename Deleter, typename T>
-Deleter* get_deleter(const not_null_shared_ptr<T>& ptr) {
+template <typename Deleter, typename T, typename NullHandlerT>
+Deleter* get_deleter(const not_null_shared_ptr<T, NullHandlerT>& ptr) {
   return std::get_deleter<Deleter>(ptr.unwrap());
 }
 
 /**
  * Casting
  */
-template <typename T, typename U>
-not_null_shared_ptr<T> static_pointer_cast(const not_null_shared_ptr<U>& r) {
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT,
+    typename UNullHandlerT>
+not_null_shared_ptr<T, TNullHandlerT> static_pointer_cast(
+    const not_null_shared_ptr<U, UNullHandlerT>& r) {
   auto p = std::static_pointer_cast<T, U>(r.unwrap());
-  return not_null_shared_ptr<T>(
+  return not_null_shared_ptr<T, TNullHandlerT>(
       std::move(p), detail::secret_guaranteed_not_null::get());
 }
-template <typename T, typename U>
-not_null_shared_ptr<T> static_pointer_cast(not_null_shared_ptr<U>&& r) {
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT,
+    typename UNullHandlerT>
+not_null_shared_ptr<T, TNullHandlerT> static_pointer_cast(
+    not_null_shared_ptr<U, UNullHandlerT>&& r) {
   auto p = std::static_pointer_cast<T, U>(std::move(r).unwrap());
-  return not_null_shared_ptr<T>(
+  return not_null_shared_ptr<T, TNullHandlerT>(
       std::move(p), detail::secret_guaranteed_not_null::get());
 }
-template <typename T, typename U>
-std::shared_ptr<T> dynamic_pointer_cast(const not_null_shared_ptr<U>& r) {
+template <typename T, typename U, typename UNullHandlerT>
+std::shared_ptr<T> dynamic_pointer_cast(
+    const not_null_shared_ptr<U, UNullHandlerT>& r) {
   return std::dynamic_pointer_cast<T, U>(r.unwrap());
 }
-template <typename T, typename U>
-std::shared_ptr<T> dynamic_pointer_cast(not_null_shared_ptr<U>&& r) {
+template <typename T, typename U, typename UNullHandlerT>
+std::shared_ptr<T> dynamic_pointer_cast(
+    not_null_shared_ptr<U, UNullHandlerT>&& r) {
   return std::dynamic_pointer_cast<T, U>(std::move(r).unwrap());
 }
-template <typename T, typename U>
-not_null_shared_ptr<T> const_pointer_cast(const not_null_shared_ptr<U>& r) {
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT,
+    typename UNullHandlerT>
+not_null_shared_ptr<T, TNullHandlerT> const_pointer_cast(
+    const not_null_shared_ptr<U, UNullHandlerT>& r) {
   auto p = std::const_pointer_cast<T, U>(r.unwrap());
-  return not_null_shared_ptr<T>(
+  return not_null_shared_ptr<T, TNullHandlerT>(
       std::move(p), detail::secret_guaranteed_not_null::get());
 }
-template <typename T, typename U>
-not_null_shared_ptr<T> const_pointer_cast(not_null_shared_ptr<U>&& r) {
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT,
+    typename UNullHandlerT>
+not_null_shared_ptr<T, TNullHandlerT> const_pointer_cast(
+    not_null_shared_ptr<U, UNullHandlerT>&& r) {
   auto p = std::const_pointer_cast<T, U>(std::move(r).unwrap());
-  return not_null_shared_ptr<T>(
+  return not_null_shared_ptr<T, TNullHandlerT>(
       std::move(p), detail::secret_guaranteed_not_null::get());
 }
-template <typename T, typename U>
-not_null_shared_ptr<T> reinterpret_pointer_cast(
-    const not_null_shared_ptr<U>& r) {
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT,
+    typename UNullHandlerT>
+not_null_shared_ptr<T, TNullHandlerT> reinterpret_pointer_cast(
+    const not_null_shared_ptr<U, UNullHandlerT>& r) {
   auto p = std::reinterpret_pointer_cast<T, U>(r.unwrap());
-  return not_null_shared_ptr<T>(
+  return not_null_shared_ptr<T, TNullHandlerT>(
       std::move(p), detail::secret_guaranteed_not_null::get());
 }
-template <typename T, typename U>
-not_null_shared_ptr<T> reinterpret_pointer_cast(not_null_shared_ptr<U>&& r) {
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT,
+    typename UNullHandlerT>
+not_null_shared_ptr<T, TNullHandlerT> reinterpret_pointer_cast(
+    not_null_shared_ptr<U, UNullHandlerT>&& r) {
   auto p = std::reinterpret_pointer_cast<T, U>(std::move(r).unwrap());
-  return not_null_shared_ptr<T>(
+  return not_null_shared_ptr<T, TNullHandlerT>(
       std::move(p), detail::secret_guaranteed_not_null::get());
 }
 
diff --git a/folly/folly/memory/not_null.h b/folly/folly/memory/not_null.h
--- a/folly/folly/memory/not_null.h
+++ b/folly/folly/memory/not_null.h
@@ -86,12 +86,24 @@
   struct guaranteed_not_null {};
 };
 
+class default_null_handler {
+ public:
+  // Handle termination when the invariant of the pointer being null is
+  // violated, such as when doing debug checking after moving from a not_null
+  // smart pointer.
+  [[noreturn]] static inline void handle_terminate(const char* msg);
+
+  // Handle when code attempts to construct or assign a not_null object from a
+  // null pointer.
+  [[noreturn]] static inline void handle_throw(const char* msg);
+};
+
 /**
  * not_null_base, the common interface for all not_null subclasses.
  *  - Implicitly constructs and casts just like a PtrT.
  *  - Has unwrap() function to access the underlying PtrT.
  */
-template <typename PtrT>
+template <typename PtrT, typename NullHandlerT>
 class not_null_base : protected guaranteed_not_null_provider {
   template <bool>
   struct implicit_tag {};
@@ -110,7 +122,11 @@
    */
   not_null_base() = delete;
   /* implicit */ not_null_base(std::nullptr_t) = delete;
+  ~not_null_base() = default;
 
+  // Copy and move constructors from other not_null_base types doesn't do any
+  // null checking and thus will only throw if the underlying type's constructor
+  // throws.
   not_null_base(const not_null_base& nn) = default;
   not_null_base(not_null_base&& nn) = default;
 
@@ -215,12 +231,12 @@
  *
  * Default implementation is not_null_base.
  */
-template <typename PtrT>
-class not_null : public not_null_base<PtrT> {
+template <typename PtrT, typename NullHandlerT = default_null_handler>
+class not_null : public not_null_base<PtrT, NullHandlerT> {
  public:
-  using pointer = typename not_null_base<PtrT>::pointer;
-  using element_type = typename not_null_base<PtrT>::element_type;
-  using not_null_base<PtrT>::not_null_base;
+  using pointer = typename not_null_base<PtrT, NullHandlerT>::pointer;
+  using element_type = typename not_null_base<PtrT, NullHandlerT>::element_type;
+  using not_null_base<PtrT, NullHandlerT>::not_null_base;
 };
 
 /**
@@ -238,18 +254,19 @@
  * Notes:
  *  - Has make_not_null_unique, equivalent to std::make_unique
  */
-template <typename T, typename Deleter>
-class not_null<std::unique_ptr<T, Deleter>>
-    : public not_null_base<std::unique_ptr<T, Deleter>> {
+template <typename T, typename Deleter, typename NullHandlerT>
+class not_null<std::unique_ptr<T, Deleter>, NullHandlerT>
+    : public not_null_base<std::unique_ptr<T, Deleter>, NullHandlerT> {
  public:
-  using pointer = not_null<typename std::unique_ptr<T, Deleter>::pointer>;
+  using pointer =
+      not_null<typename std::unique_ptr<T, Deleter>::pointer, NullHandlerT>;
   using element_type = typename std::unique_ptr<T, Deleter>::element_type;
   using deleter_type = typename std::unique_ptr<T, Deleter>::deleter_type;
 
   /**
    * Constructors. Most are inherited from not_null_base.
    */
-  using not_null_base<std::unique_ptr<T, Deleter>>::not_null_base;
+  using not_null_base<std::unique_ptr<T, Deleter>, NullHandlerT>::not_null_base;
 
   not_null(pointer p, const Deleter& d);
   not_null(pointer p, Deleter&& d);
@@ -282,8 +299,11 @@
   const Deleter& get_deleter() const noexcept;
 };
 
-template <typename T, typename Deleter = std::default_delete<T>>
-using not_null_unique_ptr = not_null<std::unique_ptr<T, Deleter>>;
+template <
+    typename T,
+    typename Deleter = std::default_delete<T>,
+    typename NullHandlerT = default_null_handler>
+using not_null_unique_ptr = not_null<std::unique_ptr<T, Deleter>, NullHandlerT>;
 
 template <typename T, typename... Args>
 not_null_unique_ptr<T> make_not_null_unique(Args&&... args);
@@ -303,41 +323,50 @@
  * Notes:
  *  - Has make_not_null_shared, equivalent to std::make_shared.
  */
-template <typename T>
-class not_null<std::shared_ptr<T>> : public not_null_base<std::shared_ptr<T>> {
+template <typename T, typename NullHandlerT>
+class not_null<std::shared_ptr<T>, NullHandlerT>
+    : public not_null_base<std::shared_ptr<T>, NullHandlerT> {
  public:
   using element_type = typename std::shared_ptr<T>::element_type;
-  using pointer = not_null<element_type*>;
+  using pointer = not_null<element_type*, NullHandlerT>;
   using weak_type = typename std::shared_ptr<T>::weak_type;
 
   /**
    * Constructors. Most are inherited from not_null_base.
    */
-  using not_null_base<std::shared_ptr<T>>::not_null_base;
+  using not_null_base<std::shared_ptr<T>, NullHandlerT>::not_null_base;
 
   template <typename U, typename Deleter>
   not_null(U* ptr, Deleter d);
-  template <typename U, typename Deleter>
-  not_null(not_null<U*> ptr, Deleter d);
+  template <typename U, typename Deleter, typename UNullHandlerT>
+  not_null(not_null<U*, UNullHandlerT> ptr, Deleter d);
 
   /**
    * Aliasing constructors.
    *
-   * Note:
+   * Notes:
    *  - The aliased shared_ptr argument, @r, is allowed to be null. The
    *    constructed object is not null iff @ptr is.
+   *  - Don't template on the null handler of raw value not_null ptr wrappers
+   *    so that we get automatic implicit conversion to the null handler of this
+   *    destination type when aliasing raw pointer values.
    */
   template <typename U>
-  not_null(const std::shared_ptr<U>& r, not_null<element_type*> ptr) noexcept;
-  template <typename U>
   not_null(
-      const not_null<std::shared_ptr<U>>& r,
-      not_null<element_type*> ptr) noexcept;
-  template <typename U>
-  not_null(std::shared_ptr<U>&& r, not_null<element_type*> ptr) noexcept;
+      const std::shared_ptr<U>& r,
+      not_null<element_type*, NullHandlerT> ptr) noexcept;
+  template <typename U, typename UNullHandlerT>
+  not_null(
+      const not_null<std::shared_ptr<U>, UNullHandlerT>& r,
+      not_null<element_type*, NullHandlerT> ptr) noexcept;
   template <typename U>
   not_null(
-      not_null<std::shared_ptr<U>>&& r, not_null<element_type*> ptr) noexcept;
+      std::shared_ptr<U>&& r,
+      not_null<element_type*, NullHandlerT> ptr) noexcept;
+  template <typename U, typename UNullHandlerT>
+  not_null(
+      not_null<std::shared_ptr<U>, UNullHandlerT>&& r,
+      not_null<element_type*, NullHandlerT> ptr) noexcept;
 
   /**
    * not_null_shared_ptr can only be reset to a non-null pointer.
@@ -345,12 +374,12 @@
   void reset() = delete;
   template <typename U>
   void reset(U* ptr);
-  template <typename U>
-  void reset(not_null<U*> ptr) noexcept;
+  template <typename U, typename UNullHandlerT>
+  void reset(not_null<U*, UNullHandlerT> ptr) noexcept;
   template <typename U, typename Deleter>
   void reset(U* ptr, Deleter d);
-  template <typename U, typename Deleter>
-  void reset(not_null<U*> ptr, Deleter d);
+  template <typename U, typename Deleter, typename UNullHandlerT>
+  void reset(not_null<U*, UNullHandlerT> ptr, Deleter d);
 
   /**
    * get() returns a not_null.
@@ -376,12 +405,13 @@
   long use_count() const noexcept;
   template <typename U>
   bool owner_before(const std::shared_ptr<U>& other) const noexcept;
-  template <typename U>
-  bool owner_before(const not_null<std::shared_ptr<U>>& other) const noexcept;
+  template <typename U, typename UNullHandlerT>
+  bool owner_before(
+      const not_null<std::shared_ptr<U>, UNullHandlerT>& other) const noexcept;
 };
 
-template <typename T>
-using not_null_shared_ptr = not_null<std::shared_ptr<T>>;
+template <typename T, typename NullHandlerT = default_null_handler>
+using not_null_shared_ptr = not_null<std::shared_ptr<T>, NullHandlerT>;
 
 template <typename T, typename... Args>
 not_null_shared_ptr<T> make_not_null_shared(Args&&... args);
@@ -396,14 +426,15 @@
  *  - Works when one of the operands is not not_null.
  *  - Works when one of the operands is nullptr.
  */
-#define FB_NOT_NULL_MK_OP(op)                                      \
-  template <typename PtrT, typename T>                             \
-  bool operator op(const not_null<PtrT>& lhs, const T& rhs);       \
-  template <                                                       \
-      typename PtrT,                                               \
-      typename T,                                                  \
-      typename = std::enable_if_t<!detail::is_not_null<T>::value>> \
-  bool operator op(const T& lhs, const not_null<PtrT>& rhs);
+#define FB_NOT_NULL_MK_OP(op)                                                 \
+  template <typename PtrT, typename T, typename LhsNullHandlerT>              \
+  bool operator op(const not_null<PtrT, LhsNullHandlerT>& lhs, const T& rhs); \
+  template <                                                                  \
+      typename PtrT,                                                          \
+      typename T,                                                             \
+      typename RhsNullHandlerT,                                               \
+      typename = std::enable_if_t<!detail::is_not_null<T>::value>>            \
+  bool operator op(const T& lhs, const not_null<PtrT, RhsNullHandlerT>& rhs);
 FB_NOT_NULL_MK_OP(==)
 FB_NOT_NULL_MK_OP(!=)
 FB_NOT_NULL_MK_OP(<)
@@ -416,9 +447,9 @@
  * Output:
  *  - Forwards to underlying PtrT.
  */
-template <typename U, typename V, typename PtrT>
+template <typename U, typename V, typename PtrT, typename NullHandlerT>
 std::basic_ostream<U, V>& operator<<(
-    std::basic_ostream<U, V>& os, const not_null<PtrT>& ptr);
+    std::basic_ostream<U, V>& os, const not_null<PtrT, NullHandlerT>& ptr);
 
 /**
  * Swap
@@ -429,29 +460,60 @@
 /**
  * Getters
  */
-template <typename Deleter, typename T>
-Deleter* get_deleter(const not_null_shared_ptr<T>& ptr);
+template <typename Deleter, typename T, typename NullHandlerT>
+Deleter* get_deleter(const not_null_shared_ptr<T, NullHandlerT>& ptr);
 
 /**
  * Casting
  */
-template <typename T, typename U>
-not_null_shared_ptr<T> static_pointer_cast(const not_null_shared_ptr<U>& r);
-template <typename T, typename U>
-not_null_shared_ptr<T> static_pointer_cast(not_null_shared_ptr<U>&& r);
-template <typename T, typename U>
-std::shared_ptr<T> dynamic_pointer_cast(const not_null_shared_ptr<U>& r);
-template <typename T, typename U>
-std::shared_ptr<T> dynamic_pointer_cast(not_null_shared_ptr<U>&& r);
-template <typename T, typename U>
-not_null_shared_ptr<T> const_pointer_cast(const not_null_shared_ptr<U>& r);
-template <typename T, typename U>
-not_null_shared_ptr<T> const_pointer_cast(not_null_shared_ptr<U>&& r);
-template <typename T, typename U>
-not_null_shared_ptr<T> reinterpret_pointer_cast(
-    const not_null_shared_ptr<U>& r);
-template <typename T, typename U>
-not_null_shared_ptr<T> reinterpret_pointer_cast(not_null_shared_ptr<U>&& r);
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT = default_null_handler,
+    typename UNullHandlerT = default_null_handler>
+not_null_shared_ptr<T, TNullHandlerT> static_pointer_cast(
+    const not_null_shared_ptr<U, UNullHandlerT>& r);
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT = default_null_handler,
+    typename UNullHandlerT = default_null_handler>
+not_null_shared_ptr<T, TNullHandlerT> static_pointer_cast(
+    not_null_shared_ptr<U, UNullHandlerT>&& r);
+template <typename T, typename U, typename UNullHandlerT = default_null_handler>
+std::shared_ptr<T> dynamic_pointer_cast(
+    const not_null_shared_ptr<U, UNullHandlerT>& r);
+template <typename T, typename U, typename UNullHandlerT = default_null_handler>
+std::shared_ptr<T> dynamic_pointer_cast(
+    not_null_shared_ptr<U, UNullHandlerT>&& r);
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT = default_null_handler,
+    typename UNullHandlerT = default_null_handler>
+not_null_shared_ptr<T, TNullHandlerT> const_pointer_cast(
+    const not_null_shared_ptr<U, UNullHandlerT>& r);
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT = default_null_handler,
+    typename UNullHandlerT = default_null_handler>
+not_null_shared_ptr<T, TNullHandlerT> const_pointer_cast(
+    not_null_shared_ptr<U, UNullHandlerT>&& r);
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT = default_null_handler,
+    typename UNullHandlerT = default_null_handler>
+not_null_shared_ptr<T, TNullHandlerT> reinterpret_pointer_cast(
+    const not_null_shared_ptr<U, UNullHandlerT>& r);
+template <
+    typename T,
+    typename U,
+    typename TNullHandlerT = default_null_handler,
+    typename UNullHandlerT = default_null_handler>
+not_null_shared_ptr<T, TNullHandlerT> reinterpret_pointer_cast(
+    not_null_shared_ptr<U, UNullHandlerT>&& r);
 
 template <typename PtrT>
 not_null(PtrT&&) -> not_null<std::remove_cv_t<std::remove_reference_t<PtrT>>>;
@@ -463,8 +525,8 @@
  * Hashing:
  *  - Forwards to underlying PtrT.
  */
-template <typename PtrT>
-struct hash<::folly::not_null<PtrT>> : hash<PtrT> {};
+template <typename PtrT, typename NullHandlerT>
+struct hash<::folly::not_null<PtrT, NullHandlerT>> : hash<PtrT> {};
 } // namespace std
 
 #include <folly/memory/not_null-inl.h>
diff --git a/folly/folly/memset_select_aarch64.cpp b/folly/folly/memset_select_aarch64.cpp
deleted file mode 100644
--- a/folly/folly/memset_select_aarch64.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * How on earth does this work?
- *
- * See memcpy_select_aarch64.cpp for a full discussion.
- */
-
-#include <cstddef>
-#include <cstdint>
-
-#if defined(__linux__) && defined(__aarch64__)
-
-#include <asm/hwcap.h> // @manual
-
-#if __has_include(<sys/ifunc.h>)
-#include <sys/ifunc.h>
-#endif
-
-#if !defined(HWCAP2_MOPS)
-#define HWCAP2_MOPS (1UL << 43)
-#endif
-
-extern "C" {
-
-void* __folly_memset_aarch64_mops(void* dest, int ch, std::size_t count);
-void* __folly_memset_aarch64_simd(void* dest, int ch, std::size_t count);
-
-[[gnu::no_sanitize_address]]
-decltype(&__folly_memset_aarch64_simd) __folly_detail_memset_resolve(
-    uint64_t hwcaps, const void* arg2) {
-#if defined(_IFUNC_ARG_HWCAP)
-  if (hwcaps & _IFUNC_ARG_HWCAP && arg2 != nullptr) {
-    const __ifunc_arg_t* args = reinterpret_cast<const __ifunc_arg_t*>(arg2);
-    if (args->_hwcap2 & HWCAP2_MOPS) {
-      return __folly_memset_aarch64_mops;
-    }
-  }
-#endif
-
-  return __folly_memset_aarch64_simd;
-}
-
-[[gnu::ifunc("__folly_detail_memset_resolve")]]
-void* __folly_memset(void* dest, int ch, std::size_t count);
-
-#ifdef FOLLY_MEMSET_IS_MEMSET
-
-[[gnu::weak, gnu::alias("__folly_memset")]]
-void* memset(void* dest, int ch, std::size_t count);
-
-#endif
-
-} // extern "C"
-
-#endif // defined(__linux__) && defined(__aarch64__)
diff --git a/folly/folly/net/NetOps.cpp b/folly/folly/net/NetOps.cpp
--- a/folly/folly/net/NetOps.cpp
+++ b/folly/folly/net/NetOps.cpp
@@ -110,8 +110,9 @@
 #if defined(__EMSCRIPTEN__)
   throw std::logic_error("Not implemented!");
 #else
-  return NetworkSocket(wrapSocketFunction<NetworkSocket::native_handle_type>(
-      ::accept, s, addr, addrlen));
+  return NetworkSocket(
+      wrapSocketFunction<NetworkSocket::native_handle_type>(
+          ::accept, s, addr, addrlen));
 #endif
 }
 
@@ -434,10 +435,10 @@
 #endif
 }
 
-[[maybe_unused]] static ssize_t fakeSendmsg(
+#ifdef _WIN32
+static ssize_t fakeSendmsg(
     [[maybe_unused]] NetworkSocket socket,
     [[maybe_unused]] const msghdr* message) {
-#ifdef _WIN32
   SOCKET h = socket.data;
   ssize_t bytesSent = 0;
   for (size_t i = 0; i < message->msg_iovlen; i++) {
@@ -467,10 +468,8 @@
     bytesSent += r;
   }
   return bytesSent;
-#else
-  throw std::logic_error("Not implemented!");
-#endif
 }
+#endif
 
 #ifdef _WIN32
 [[maybe_unused]] ssize_t wsaSendMsgDirect(
@@ -487,11 +486,11 @@
 }
 #endif
 
-[[maybe_unused]] static ssize_t wsaSendMsg(
+#ifdef _WIN32
+static ssize_t wsaSendMsg(
     [[maybe_unused]] NetworkSocket socket,
     [[maybe_unused]] const msghdr* message,
     [[maybe_unused]] int flags) {
-#ifdef _WIN32
   // Translate msghdr to WSAMSG.
   WSAMSG msg;
   msg.name = (LPSOCKADDR)message->msg_name;
@@ -509,10 +508,8 @@
     msg.lpBuffers[i].len = (ULONG)message->msg_iov[i].iov_len;
   }
   return wsaSendMsgDirect(socket, &msg);
-#else
-  throw std::logic_error("Not implemented!");
-#endif
 }
+#endif
 
 ssize_t sendmsg(NetworkSocket socket, const msghdr* message, int flags) {
 #ifdef _WIN32
@@ -776,6 +773,21 @@
     return -1;
   }
   return fcntl(s.data, F_SETFL, flags | O_NONBLOCK);
+#endif
+}
+
+int set_socket_blocking(NetworkSocket s) {
+#ifdef _WIN32
+  u_long nonBlockingEnabled = 0;
+  return ioctlsocket(s.data, FIONBIO, &nonBlockingEnabled);
+#elif defined(__EMSCRIPTEN__)
+  throw std::logic_error("Not implemented!");
+#else
+  int flags = fcntl(s.data, F_GETFL, 0);
+  if (flags == -1) {
+    return -1;
+  }
+  return fcntl(s.data, F_SETFL, flags & ~O_NONBLOCK);
 #endif
 }
 
diff --git a/folly/folly/net/NetOps.h b/folly/folly/net/NetOps.h
--- a/folly/folly/net/NetOps.h
+++ b/folly/folly/net/NetOps.h
@@ -40,7 +40,6 @@
 #define SOL_UDP 0x0
 #define UDP_SEGMENT 0x0
 #define IP_BIND_ADDRESS_NO_PORT 0
-#define TCP_ZEROCOPY_RECEIVE 0
 
 // We don't actually support either of these flags
 // currently.
@@ -118,6 +117,10 @@
 #endif
 #endif
 
+#ifndef FOLLY_HAVE_SO_TIMESTAMPING
+#define FOLLY_HAVE_SO_TIMESTAMPING 0
+#endif
+
 #ifndef SO_EE_ORIGIN_ZEROCOPY
 #define SO_EE_ORIGIN_ZEROCOPY 5
 #endif
@@ -352,6 +355,7 @@
 // And now we diverge from the Posix way of doing things and just do things
 // our own way.
 int set_socket_non_blocking(NetworkSocket s);
+int set_socket_blocking(NetworkSocket s);
 int set_socket_close_on_exec(NetworkSocket s);
 
 #ifdef _WIN32
diff --git a/folly/folly/net/NetOpsDispatcher.cpp b/folly/folly/net/NetOpsDispatcher.cpp
--- a/folly/folly/net/NetOpsDispatcher.cpp
+++ b/folly/folly/net/NetOpsDispatcher.cpp
@@ -147,6 +147,10 @@
   return folly::netops::set_socket_non_blocking(s);
 }
 
+int Dispatcher::set_socket_blocking(NetworkSocket s) {
+  return folly::netops::set_socket_blocking(s);
+}
+
 int Dispatcher::set_socket_close_on_exec(NetworkSocket s) {
   return folly::netops::set_socket_close_on_exec(s);
 }
diff --git a/folly/folly/net/NetOpsDispatcher.h b/folly/folly/net/NetOpsDispatcher.h
--- a/folly/folly/net/NetOpsDispatcher.h
+++ b/folly/folly/net/NetOpsDispatcher.h
@@ -84,6 +84,7 @@
       int domain, int type, int protocol, NetworkSocket sv[2]);
 
   virtual int set_socket_non_blocking(NetworkSocket s);
+  virtual int set_socket_blocking(NetworkSocket s);
   virtual int set_socket_close_on_exec(NetworkSocket s);
 
  protected:
diff --git a/folly/folly/net/TcpInfo.h b/folly/folly/net/TcpInfo.h
--- a/folly/folly/net/TcpInfo.h
+++ b/folly/folly/net/TcpInfo.h
@@ -217,7 +217,7 @@
    */
   template <typename T1, typename T2>
   static const T1* getFieldAsPtr(
-      const T2& tgtStruct, const int tgtBytesRead, T1 T2::*field) {
+      const T2& tgtStruct, const int tgtBytesRead, T1 T2::* field) {
     if (field != nullptr && tgtBytesRead > 0 &&
         getTcpInfoFieldOffset(field) + sizeof(tgtStruct.*field) <=
             (unsigned long)tgtBytesRead) {
@@ -236,7 +236,7 @@
    *    https://gist.github.com/graphitemaster/494f21190bb2c63c5516
    */
   template <typename T1, typename T2>
-  static size_t constexpr getTcpInfoFieldOffset(T1 T2::*field) {
+  static size_t constexpr getTcpInfoFieldOffset(T1 T2::* field) {
     static_assert(
         std::is_standard_layout<T1>() && std::is_trivial<T1>(),
         "Object type is not standard layout or trivial");
@@ -293,7 +293,7 @@
    * use accessors, such as bytesSent().
    */
   template <typename T1>
-  const T1* getFieldAsPtr(T1 tcp_info::*field) const {
+  const T1* getFieldAsPtr(T1 tcp_info::* field) const {
     return getFieldAsPtr(tcpInfo, tcpInfoBytesRead, field);
   }
 
@@ -307,7 +307,7 @@
    * use accessors such as bytesSent().
    */
   template <typename T1>
-  folly::Optional<uint64_t> getFieldAsOptUInt64(T1 tcp_info::*field) const {
+  folly::Optional<uint64_t> getFieldAsOptUInt64(T1 tcp_info::* field) const {
     if (auto ptr = getFieldAsPtr(field)) {
       return *ptr;
     }
@@ -342,7 +342,8 @@
    * specifics, use accessors such as bbrBwBitsPerSecond().
    */
   template <typename T1>
-  folly::Optional<uint64_t> getFieldAsOptUInt64(T1 tcp_bbr_info::*field) const {
+  folly::Optional<uint64_t> getFieldAsOptUInt64(
+      T1 tcp_bbr_info::* field) const {
     if (maybeCcInfo.has_value() && ccNameEnum() == CongestionControlName::BBR) {
       return getFieldAsOptUInt64(maybeCcInfo.value().bbr, field);
     }
@@ -357,7 +358,7 @@
    */
   template <typename T1>
   folly::Optional<uint64_t> getFieldAsOptUInt64(
-      T1 tcpvegas_info::*field) const {
+      T1 tcpvegas_info::* field) const {
     if (maybeCcInfo.hasValue() &&
         ccNameEnum() == CongestionControlName::VEGAS) {
       return getFieldAsOptUInt64(maybeCcInfo.value().vegas, field);
@@ -373,7 +374,7 @@
    */
   template <typename T1>
   const folly::Optional<uint64_t> getFieldAsOptUInt64(
-      T1 tcp_dctcp_info::*field) const {
+      T1 tcp_dctcp_info::* field) const {
     if (maybeCcInfo.has_value() &&
         (ccNameEnum() == CongestionControlName::DCTCP ||
          ccNameEnum() == CongestionControlName::DCTCP_CUBIC ||
@@ -395,7 +396,7 @@
    */
   template <typename T1, typename T2>
   folly::Optional<uint64_t> getFieldAsOptUInt64(
-      const T2& tgtStruct, T1 T2::*field) const {
+      const T2& tgtStruct, T1 T2::* field) const {
     if (field != nullptr && tcpCcInfoBytesRead > 0 &&
         getTcpInfoFieldOffset(field) + sizeof(tgtStruct.*field) <=
             (unsigned long)tcpCcInfoBytesRead) {
diff --git a/folly/folly/observer/CoreCachedObserver.h b/folly/folly/observer/CoreCachedObserver.h
--- a/folly/folly/observer/CoreCachedObserver.h
+++ b/folly/folly/observer/CoreCachedObserver.h
@@ -70,6 +70,8 @@
   }
   CoreCachedSnapshot operator*() const { return getSnapshot(); }
 
+  const Observer<T>& getUnderlyingObserver() const { return observer_; }
+
  private:
   Observer<T> observer_;
   AtomicCoreCachedSharedPtr<const T> data_;
diff --git a/folly/folly/observer/HazptrObserver.h b/folly/folly/observer/HazptrObserver.h
--- a/folly/folly/observer/HazptrObserver.h
+++ b/folly/folly/observer/HazptrObserver.h
@@ -89,15 +89,19 @@
         updateObserver_(
             makeObserver([o = std::move(observer), alive = alive_, this]() {
               auto snapshot = o.getSnapshot();
-              auto oldState = static_cast<State*>(nullptr);
-              alive->withRLock([&](auto vAlive) {
-                if (vAlive) { // otherwise state_ may be out-of-scope
-                  oldState = state_.exchange(
-                      new State(snapshot), std::memory_order_acq_rel);
-                }
-              });
-              if (oldState) {
-                oldState->retire(domain_);
+              auto alivePtr = alive->rlock();
+              if (!*alivePtr) {
+                return folly::unit; // This instance has already been destroyed
+              }
+              auto oldState = state_.exchange(
+                  new State(snapshot), std::memory_order_acq_rel);
+              auto& domain = domain_;
+              // Release the alive lock before retiring the old state to avoid a
+              // potential deadlock due to inline hazptr reclamation
+              alivePtr.unlock();
+              // At this point, this instance may have already been destroyed
+              if (oldState != nullptr) {
+                oldState->retire(domain);
               }
               return folly::unit;
             })) {}
diff --git a/folly/folly/observer/ReadMostlyTLObserver.h b/folly/folly/observer/ReadMostlyTLObserver.h
--- a/folly/folly/observer/ReadMostlyTLObserver.h
+++ b/folly/folly/observer/ReadMostlyTLObserver.h
@@ -48,7 +48,7 @@
     return refresh();
   }
 
-  Observer<T> getUnderlyingObserver() const { return observer_; }
+  const Observer<T>& getUnderlyingObserver() const { return observer_; }
 
  private:
   ReadMostlySharedPtr<const T> refresh() const {
diff --git a/folly/folly/observer/WithJitter-inl.h b/folly/folly/observer/WithJitter-inl.h
--- a/folly/folly/observer/WithJitter-inl.h
+++ b/folly/folly/observer/WithJitter-inl.h
@@ -50,8 +50,9 @@
         std::chrono::milliseconds lag,
         std::chrono::milliseconds jitter)
         : observer_(std::move(observer)),
-          state_(std::make_shared<Synchronized<State, std::mutex>>(
-              State(observer_.getSnapshot().getShared()))),
+          state_(
+              std::make_shared<Synchronized<State, std::mutex>>(
+                  State(observer_.getSnapshot().getShared()))),
           lag_(lag),
           jitter_(jitter) {}
 
diff --git a/folly/folly/observer/detail/Core.h b/folly/folly/observer/detail/Core.h
--- a/folly/folly/observer/detail/Core.h
+++ b/folly/folly/observer/detail/Core.h
@@ -63,7 +63,7 @@
     static CreatorContext create(const F& creator) {
       CreatorContext context;
       context.typeInfo = &typeid(F);
-      context.invokeResultTypeInfo = &typeid(std::invoke_result_t<F>);
+      context.invokeResultTypeInfo = &typeid(decltype(FOLLY_DECLVAL(F&&)()));
       if constexpr (Has_getNameT_v<F>) {
         context.name = creator.getName();
       }
diff --git a/folly/folly/observer/detail/ObserverManager.cpp b/folly/folly/observer/detail/ObserverManager.cpp
--- a/folly/folly/observer/detail/ObserverManager.cpp
+++ b/folly/folly/observer/detail/ObserverManager.cpp
@@ -62,6 +62,7 @@
       LOG(ERROR) << "--observer_manager_pool_size should be >= 1";
       FLAGS_observer_manager_pool_size = 1;
     }
+    threads_.reserve(FLAGS_observer_manager_pool_size);
     for (int32_t i = 0; i < FLAGS_observer_manager_pool_size; ++i) {
       threads_.emplace_back([this, i]() {
         folly::setThreadName(
@@ -87,6 +88,11 @@
     }
   }
 
+  CurrentQueueProcessor(const CurrentQueueProcessor&) = delete;
+  CurrentQueueProcessor& operator=(const CurrentQueueProcessor&) = delete;
+  CurrentQueueProcessor(CurrentQueueProcessor&&) = delete;
+  CurrentQueueProcessor& operator=(CurrentQueueProcessor&&) = delete;
+
   ~CurrentQueueProcessor() {
     for (size_t i = 0; i < threads_.size(); ++i) {
       queue_.enqueue(nullptr);
@@ -156,6 +162,11 @@
       }
     });
   }
+
+  NextQueueProcessor(const NextQueueProcessor&) = delete;
+  NextQueueProcessor& operator=(const NextQueueProcessor&) = delete;
+  NextQueueProcessor(NextQueueProcessor&&) = delete;
+  NextQueueProcessor& operator=(NextQueueProcessor&&) = delete;
 
   ~NextQueueProcessor() {
     stop_ = true;
diff --git a/folly/folly/portability/Filesystem.cpp b/folly/folly/portability/Filesystem.cpp
--- a/folly/folly/portability/Filesystem.cpp
+++ b/folly/folly/portability/Filesystem.cpp
@@ -18,50 +18,8 @@
 
 namespace folly::fs {
 
-#if __cpp_lib_filesystem >= 201703
-
 path lexically_normal_fn::operator()(path const& p) const {
   return p.lexically_normal();
 }
-
-#elif __cpp_lib_experimental_filesystem >= 201406
-
-//  mimic: std::filesystem::path::lexically_normal, C++17
-//  from: https://github.com/gulrak/filesystem/tree/v1.5.0, MIT
-path lexically_normal_fn::operator()(path const& p) const {
-  path dest;
-  bool lastDotDot = false;
-  for (path::string_type s : p) {
-    if (s == ".") {
-      dest /= "";
-      continue;
-    } else if (s == ".." && !dest.empty()) {
-      auto root = p.root_path();
-      if (dest == root) {
-        continue;
-      } else if (*(--dest.end()) != "..") {
-        auto drepr = dest.native();
-        if (drepr.back() == path::preferred_separator) {
-          drepr.pop_back();
-          dest = std::move(drepr);
-        }
-        dest.remove_filename();
-        continue;
-      }
-    }
-    if (!(s.empty() && lastDotDot)) {
-      dest /= s;
-    }
-    lastDotDot = s == "..";
-  }
-  if (dest.empty()) {
-    dest = ".";
-  }
-  return dest;
-}
-
-#else
-#error require filesystem
-#endif
 
 } // namespace folly::fs
diff --git a/folly/folly/portability/Filesystem.h b/folly/folly/portability/Filesystem.h
--- a/folly/folly/portability/Filesystem.h
+++ b/folly/folly/portability/Filesystem.h
@@ -16,13 +16,7 @@
 
 #pragma once
 
-#if __has_include(<filesystem>)
 #include <filesystem>
-#elif __has_include(<experimental/filesystem>)
-#include <experimental/filesystem>
-#else
-#error require filesystem
-#endif
 
 namespace folly::fs {
 
@@ -31,20 +25,9 @@
   std_experimental = 2,
 };
 
-#if __cpp_lib_filesystem >= 201703
-
 namespace std_fs = std::filesystem;
 inline constexpr which_enum which = which_enum::std;
 
-#elif __cpp_lib_experimental_filesystem >= 201406
-
-namespace std_fs = std::experimental::filesystem;
-inline constexpr which_enum which = which_enum::std_experimental;
-
-#else
-#error require filesystem
-#endif
-
 //  imports
 
 using std_fs::absolute;
@@ -97,14 +80,10 @@
 using std_fs::temp_directory_path;
 using std_fs::u8path;
 
-#if __cpp_lib_filesystem >= 201703
-
 using std_fs::perm_options;
 using std_fs::proximate;
 using std_fs::relative;
 using std_fs::weakly_canonical;
-
-#endif
 
 struct lexically_normal_fn {
   path operator()(path const& p) const;
diff --git a/folly/folly/portability/GFlags.h b/folly/folly/portability/GFlags.h
--- a/folly/folly/portability/GFlags.h
+++ b/folly/folly/portability/GFlags.h
@@ -22,7 +22,7 @@
 #include <string>
 
 #if FOLLY_HAVE_LIBGFLAGS && __has_include(<gflags/gflags.h>)
-#include <gflags/gflags.h>
+#include <gflags/gflags.h> // IWYU pragma: export
 #endif
 
 #define FOLLY_GFLAGS_DECLARE_FALLBACK_(_type, _shortType, _name) \
diff --git a/folly/folly/portability/Stdlib.h b/folly/folly/portability/Stdlib.h
--- a/folly/folly/portability/Stdlib.h
+++ b/folly/folly/portability/Stdlib.h
@@ -72,7 +72,7 @@
 } // namespace portability
 } // namespace folly
 
-#if defined(_WIN32) || (!defined(__linux__) && !FOLLY_MOBILE)
+#if defined(_WIN32) || (!defined(__linux__))
 FOLLY_PUSH_WARNING
 FOLLY_CLANG_DISABLE_WARNING("-Wheader-hygiene")
 /* using override */ using namespace folly::portability::stdlib;
diff --git a/folly/folly/portability/SysMman.cpp b/folly/folly/portability/SysMman.cpp
--- a/folly/folly/portability/SysMman.cpp
+++ b/folly/folly/portability/SysMman.cpp
@@ -228,7 +228,7 @@
       VirtualQuery(addr, &inf, sizeof(inf));
       assert(inf.AllocationBase == addr);
 
-      auto deb = (MemMapDebugTrailer*)((char*)addr + length);
+      [[maybe_unused]] auto deb = (MemMapDebugTrailer*)((char*)addr + length);
       assert(deb->length == length);
       assert(deb->magic == kMMapLengthMagic);
     }
diff --git a/folly/folly/portability/SysMman.h b/folly/folly/portability/SysMman.h
--- a/folly/folly/portability/SysMman.h
+++ b/folly/folly/portability/SysMman.h
@@ -26,14 +26,22 @@
 #define MAP_ANONYMOUS MAP_ANON
 #endif
 #endif
+// New flags, may not be available yet in all libc implementations.
+#ifndef MADV_POPULATE_READ
+#define MADV_POPULATE_READ 22
+#endif // MADV_POPULATE_READ
+#ifndef MADV_COLLAPSE
+#define MADV_COLLAPSE 25
+#endif // MADV_COLLAPSE
+#ifndef MAP_POPULATE
+#define MAP_POPULATE 0
+#endif
 
-#else
+#else //_WIN32
 
 #include <cstdint>
 
-#include <sys/types.h>
-
-using off64_t = int64_t;
+#include <folly/portability/SysTypes.h>
 
 #define MAP_ANONYMOUS 1
 #define MAP_ANON MAP_ANONYMOUS
@@ -53,6 +61,8 @@
 #define MADV_NORMAL 0
 #define MADV_DONTNEED 0
 #define MADV_SEQUENTIAL 0
+#define MADV_POPULATE_READ 0
+#define MADV_COLLAPSE 0
 
 extern "C" {
 int madvise(const void* addr, size_t len, int advise);
diff --git a/folly/folly/portability/SysSyscall.h b/folly/folly/portability/SysSyscall.h
--- a/folly/folly/portability/SysSyscall.h
+++ b/folly/folly/portability/SysSyscall.h
@@ -56,7 +56,8 @@
 //
 //  mimic: syscall(2), linux
 template <typename... A>
-FOLLY_ERASE long linux_syscall(long number, A... a) {
+FOLLY_ERASE long linux_syscall(
+    [[maybe_unused]] long number, [[maybe_unused]] A... a) {
 #if defined(_WIN32) || (defined(__EMSCRIPTEN__) && !defined(syscall))
   errno = ENOSYS;
   return -1;
diff --git a/folly/folly/portability/SysTypes.h b/folly/folly/portability/SysTypes.h
--- a/folly/folly/portability/SysTypes.h
+++ b/folly/folly/portability/SysTypes.h
@@ -20,6 +20,7 @@
 
 #ifdef _WIN32
 #include <basetsd.h> // @manual
+#include <inttypes.h>
 
 // This is a massive pain to have be an `int` due to the pthread implementation
 // we support, but it's far more compatible with the rest of the windows world
@@ -28,6 +29,8 @@
 
 using uid_t = int;
 using gid_t = int;
+
+using off64_t = int64_t;
 
 // This isn't actually supposed to be defined here, but it's the most
 // appropriate place without defining a portability header for stdint.h
diff --git a/folly/folly/portability/Time.cpp b/folly/folly/portability/Time.cpp
--- a/folly/folly/portability/Time.cpp
+++ b/folly/folly/portability/Time.cpp
@@ -156,17 +156,6 @@
 
   // FILETIMEs are in units of 100ns.
   return unsigned_nanos(i.QuadPart * 100);
-};
-
-static LARGE_INTEGER performanceFrequency() {
-  static auto result = [] {
-    LARGE_INTEGER freq;
-    // On Windows XP or later, this will never fail.
-    BOOL res = QueryPerformanceFrequency(&freq);
-    assert(res);
-    return freq;
-  }();
-  return result;
 }
 
 extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
diff --git a/folly/folly/portability/Unistd.h b/folly/folly/portability/Unistd.h
--- a/folly/folly/portability/Unistd.h
+++ b/folly/folly/portability/Unistd.h
@@ -49,6 +49,7 @@
 #define _SC_PAGE_SIZE _SC_PAGESIZE
 #define _SC_NPROCESSORS_ONLN 2
 #define _SC_NPROCESSORS_CONF 2
+#define _SC_LEVEL1_DCACHE_LINESIZE 3
 
 // Windows doesn't define these, but these are the correct values
 // for Windows.
@@ -70,7 +71,6 @@
 namespace folly {
 namespace portability {
 namespace unistd {
-using off64_t = int64_t;
 int fsync(int fd);
 int ftruncate(int fd, off_t len);
 int getdtablesize();
diff --git a/folly/folly/portability/openat2.c b/folly/folly/portability/openat2.c
new file mode 100644
--- /dev/null
+++ b/folly/folly/portability/openat2.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/folly-config.h>
+
+#if FOLLY_HAVE_OPENAT2
+
+#include <folly/portability/openat2.h>
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+int openat2(int dirfd, const char* pathname, const struct open_how* how) {
+  return syscall(SYS_openat2, dirfd, pathname, how, sizeof(struct open_how));
+}
+
+#endif
diff --git a/folly/folly/python/AsyncioExecutor.h b/folly/folly/python/AsyncioExecutor.h
--- a/folly/folly/python/AsyncioExecutor.h
+++ b/folly/folly/python/AsyncioExecutor.h
@@ -16,13 +16,17 @@
 
 #pragma once
 
+#include <folly/Chrono.h>
 #include <folly/ExceptionString.h>
 #include <folly/Function.h>
 #include <folly/executors/DrivableExecutor.h>
 #include <folly/executors/SequencedExecutor.h>
 #include <folly/io/async/NotificationQueue.h>
+#include <folly/portability/GFlags.h>
 #include <folly/python/Weak.h>
 
+FOLLY_GFLAGS_DECLARE_uint32(folly_asyncio_executor_drive_time_slice_ms);
+
 namespace folly {
 namespace python {
 
@@ -99,20 +103,67 @@
       public SequencedExecutor {
  public:
   using Func = folly::Func;
+  using Clock = folly::chrono::coarse_steady_clock;
 
+  NotificationQueueAsyncioExecutor() noexcept
+      : driveTimeSlice_(getDefaultTimeSlice()) {}
+  explicit NotificationQueueAsyncioExecutor(
+      std::chrono::milliseconds driveTimeSlice) noexcept
+      : driveTimeSlice_(driveTimeSlice) {}
+
+  struct Stats {
+    size_t driveCount{0};
+  };
+
   void add(Func func) override { queue_.putMessage(std::move(func)); }
 
   int fileno() const { return consumer_.getFd(); }
 
+  /**
+   * Drive does work while messages remain in the queue
+   * for time slice duration.
+   *
+   * This ensures a healthy compromise between spending too many resources
+   * going back and forth between Python EventLoop & AsyncioExecutor,
+   * while still ensuring that we are properly interleaving Native & Python
+   * work.
+   *
+   * This is inspired from the same logic in EventBase
+   * (see setLoopCallbacksTimeslice)
+   */
   void drive() noexcept override {
-    consumer_.consume([&](Func&& func) {
-      invokeCatchingExns(
-          "NotificationQueueExecutor: task", std::exchange(func, {}));
-    });
+    ++stats_.driveCount;
+
+    // Reset current context on scope exit
+    RequestContextSaverScopeGuard ctxGuard;
+    auto deadline = Clock::now() + driveTimeSlice_;
+    // Do-while so we process at least 1 task to make forward progress
+    // (even if time_slice is set to 0).
+    do {
+      // Defined in scope, to ensure it's destroyed immediatly,
+      // while in the right request context
+      Func func;
+      // Note: tryConsume calls RequestContext::setContext().
+      // Doing this under a RequestContextSaverScopeGuard instead of a
+      // per-callback RequestContextScopeGuard to avoid switching context back
+      // and forth when consecutive callbacks have the same context.
+      if (!queue_.tryConsume(func)) {
+        break;
+      }
+      invokeCatchingExns("NotificationQueueExecutor: task", std::move(func));
+    } while (Clock::now() < deadline);
   }
 
+  const Stats& stats() const { return stats_; }
+
+ private:
   folly::NotificationQueue<Func> queue_;
   folly::NotificationQueue<Func>::SimpleConsumer consumer_{queue_};
+
+  static std::chrono::milliseconds getDefaultTimeSlice();
+  std::chrono::milliseconds driveTimeSlice_{0};
+
+  Stats stats_;
 }; // NotificationQueueAsyncioExecutor
 
 } // namespace python
diff --git a/folly/folly/python/GILAwareManualExecutor.h b/folly/folly/python/GILAwareManualExecutor.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/python/GILAwareManualExecutor.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+
+#include <folly/executors/DrivableExecutor.h>
+#include <folly/executors/SequencedExecutor.h>
+
+namespace folly {
+namespace python {
+
+/**
+ * A simple ManualExecutor intended to be run directly on a Python thread.
+ * It releases Python GIL while waiting for tasks to execute.
+ */
+class GILAwareManualExecutor
+    : public DrivableExecutor,
+      public SequencedExecutor {
+ public:
+  ~GILAwareManualExecutor() override;
+
+  void add(Func) override;
+
+  void drive() override {
+    waitBeforeDrive();
+    driveImpl();
+  }
+
+  bool keepAliveAcquire() noexcept override {
+    keepAliveCount_.fetch_add(1, std::memory_order_relaxed);
+    return true;
+  }
+
+  void keepAliveRelease() noexcept override {
+    auto keepAliveCount = keepAliveCount_.load(std::memory_order_relaxed);
+    do {
+      if (keepAliveCount == 1) {
+        add([this] {
+          // the final count *must* be released from this executor or else if we
+          // are mid-destructor we have a data race
+          keepAliveCount_.fetch_sub(1, std::memory_order_relaxed);
+        });
+        return;
+      }
+    } while (!keepAliveCount_.compare_exchange_weak(
+        keepAliveCount,
+        keepAliveCount - 1,
+        std::memory_order_release,
+        std::memory_order_relaxed));
+  }
+
+ private:
+  void waitBeforeDrive();
+  void driveImpl();
+
+  std::mutex lock_;
+  std::queue<Func> funcs_;
+  std::condition_variable cv_;
+
+  std::atomic<size_t> keepAliveCount_{0};
+};
+
+} // namespace python
+} // namespace folly
diff --git a/folly/folly/python/ProactorExecutor.h b/folly/folly/python/ProactorExecutor.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/python/ProactorExecutor.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/concurrency/ConcurrentHashMap.h>
+#include <folly/python/AsyncioExecutor.h>
+#ifdef WIN32
+#include <folly/portability/Windows.h>
+#endif
+
+namespace folly {
+namespace python {
+
+namespace detail {
+class ProactorExecutorCallback {
+ public:
+  explicit ProactorExecutorCallback(folly::Func func) noexcept
+      : func_(std::move(func)) {}
+
+  uintptr_t address() const {
+    return reinterpret_cast<uintptr_t>(&overlapped_);
+  }
+
+  void send([[maybe_unused]] uint64_t iocp) const {
+#ifdef WIN32
+    auto handle = reinterpret_cast<HANDLE>(iocp);
+    auto success =
+        PostQueuedCompletionStatus(handle, 0, 0, (LPOVERLAPPED)&overlapped_);
+    if (!success) {
+      auto what = fmt::format(
+          "Failed to notify asyncio IOCP. Errror code %d", GetLastError());
+      throw std::runtime_error(what);
+    }
+#endif
+  }
+
+  void invoke() {
+    folly::python::AsyncioExecutor::invokeCatchingExns(
+        "ProactorExecutor: task", std::exchange(func_, {}));
+  }
+
+ private:
+  folly::Func func_;
+#ifdef WIN32
+  _OVERLAPPED overlapped_{};
+#else
+  [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] tag_t<> overlapped_{};
+#endif
+};
+} // namespace detail
+
+class ProactorExecutor : public DroppableAsyncioExecutor<ProactorExecutor> {
+ private:
+  uint64_t iocp_;
+  folly::ConcurrentHashMap<
+      uintptr_t,
+      std::unique_ptr<detail::ProactorExecutorCallback>>
+      cache_;
+
+ public:
+  using Func = folly::Func;
+
+  explicit ProactorExecutor(uint64_t iocp);
+  void add(Func func) noexcept override;
+  void drive() noexcept override;
+  bool execute(uintptr_t address);
+};
+
+} // namespace python
+} // namespace folly
diff --git a/folly/folly/python/Weak.h b/folly/folly/python/Weak.h
--- a/folly/folly/python/Weak.h
+++ b/folly/folly/python/Weak.h
@@ -50,6 +50,7 @@
 Py_Weak(char*) PyBytes_AsString(PyObject*);
 Py_Weak(PyObject*)
     PyUnicode_AsEncodedString(PyObject*, const char*, const char*);
+
 Py_Weak(const char*) PyUnicode_AsUTF8(PyObject*);
 
 // Basic GIL Handling
@@ -67,6 +68,9 @@
 Py_Weak(void) _Py_DumpTraceback(int, PyThreadState*);
 Py_Weak(PyCodeObject*) PyFrame_GetCode(PyFrameObject*);
 Py_Weak(PyFrameObject*) PyFrame_GetBack(PyFrameObject*);
+
+Py_Weak(PyObject*) PyLong_FromVoidPtr(void*);
+
 #if PY_VERSION_HEX >= 0x030b0000 // >= 3.11
 Py_Weak(int) PyFrame_GetLasti(PyFrameObject*);
 #endif
@@ -81,6 +85,7 @@
 
 // Python Types
 Py_Weak(PyTypeObject) PyFrame_Type;
+Py_Weak(PyTypeObject) PyFunction_Type;
 
 #undef Py_Weak
 #endif // NOT_WINDOWS
@@ -112,7 +117,11 @@
   return (Py_IncRef != nullptr) && (Py_DecRef != nullptr) &&
       (Py_GetVersion != nullptr);
 #else
+#if defined(_WIN32)
   return true;
+#else
+  return false;
+#endif
 #endif
 }
 
diff --git a/folly/folly/python/fibers.h b/folly/folly/python/fibers.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/python/fibers.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ *  This file serves as a helper for bridging folly fibers and python
+ *  asyncio.future.
+ */
+
+#pragma once
+
+#include <folly/fibers/FiberManagerInternal.h>
+#include <folly/python/Weak.h>
+
+#ifdef FOLLY_PYTHON_WIN_SHAREDLIB
+#ifdef FOLLY_PYTHON_FIBERS_DETAIL_DEFS
+#define FOLLY_PYTHON_FIBERS_API __declspec(dllexport)
+#else
+#define FOLLY_PYTHON_FIBERS_API __declspec(dllimport)
+#endif
+#else
+#define FOLLY_PYTHON_FIBERS_API
+#endif
+
+namespace folly {
+namespace python {
+
+namespace fibers_detail {
+FOLLY_PYTHON_FIBERS_API void assign_func(
+    folly::fibers::FiberManager* (*_get_fiber_manager)(
+        const folly::fibers::FiberManager::Options&));
+} // namespace fibers_detail
+
+// Must be called from main context
+FOLLY_PYTHON_FIBERS_API folly::fibers::FiberManager* getFiberManager(
+    const folly::fibers::FiberManager::Options& opts = {});
+
+/**
+ * Helper function with similar callback/userData parameters as bridgeFuture.
+ * This can be convenient in code that calls both (notably our tests),
+ * but most callsites should directly use getFiberManager().
+ */
+template <typename T>
+void bridgeFibers(
+    folly::Function<T()>&& function,
+    folly::Function<void(folly::Try<T>&&, PyObject*)> callback,
+    PyObject* userData) {
+  auto* fiberManager = getFiberManager();
+  // We are handing over a pointer to a python object to c++ and need
+  // to make sure it isn't removed by python in that time.
+  Py_IncRef(userData);
+  auto guard = folly::makeGuard([=] { Py_DecRef(userData); });
+  fiberManager->addTask(
+      [function = std::move(function),
+       callback = std::move(callback),
+       userData,
+       guard = std::move(guard)]() mutable {
+        // This will run from inside the gil, called by the asyncio add_reader
+        auto res = folly::makeTryWith([&] { return function(); });
+        callback(std::move(res), userData);
+        // guard goes out of scope here, and its stored function is called
+      });
+}
+
+} // namespace python
+} // namespace folly
diff --git a/folly/folly/python/import.h b/folly/folly/python/import.h
--- a/folly/folly/python/import.h
+++ b/folly/folly/python/import.h
@@ -37,7 +37,7 @@
  public:
   using sig = int();
 
-  FOLLY_CONSTEVAL import_cache_nocapture(sig& fun) noexcept : fun_{fun} {}
+  consteval import_cache_nocapture(sig& fun) noexcept : fun_{fun} {}
 
   bool operator()() const {
     // protecting this with the cxxabi mutex (the mutex that
@@ -68,7 +68,7 @@
  */
 class import_cache {
  public:
-  FOLLY_CONSTEVAL import_cache(
+  consteval import_cache(
       import_cache_nocapture::sig& fun, char const* const name) noexcept
       : impl_{fun}, name_{name ? name : "<unknown>"} {}
 
diff --git a/folly/folly/python/iobuf_ext.h b/folly/folly/python/iobuf_ext.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/python/iobuf_ext.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <Python.h>
+
+#include <folly/Executor.h>
+#include <folly/io/IOBuf.h>
+
+namespace folly::python {
+
+// Returns a C++ IOBuf that shares ownership of the given Python memoryview
+// object. The C++ IOBuf can then be exposed and used as a Python IOBuf object.
+std::unique_ptr<folly::IOBuf> iobuf_from_memoryview(
+    folly::Executor* executor, PyObject* py_object, void* buf, uint64_t length);
+
+std::unique_ptr<folly::IOBuf> create_iobuf(std::size_t capacity);
+
+} // namespace folly::python
diff --git a/folly/folly/python/request_context.h b/folly/folly/python/request_context.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/python/request_context.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <Python.h>
+
+#if PY_VERSION_HEX < 0x030e0000 // < 3.14
+/*
+ *  PyContext_AddWatcher was backported to Meta Builds of Python on Linux
+ *       -DFOLLY_PYTHON_META_BUILD=1
+ */
+
+extern "C" {
+#if defined(__linux__) && FOLLY_PYTHON_META_BUILD
+// workaround local issue of missing 3.10 symbols
+__attribute__((weak)) extern int PyContext_AddWatcher(
+    PyContext_WatchCallback callback);
+#endif
+
+#ifndef FOLLY_PYTHON_META_BUILD
+/*
+ *  For those non-meta builds before 3.14 provide the basics
+ */
+
+typedef enum {
+  Py_CONTEXT_SWITCHED = 1,
+} PyContextEvent;
+
+typedef int (*PyContext_WatchCallback)(PyContextEvent, PyObject*);
+
+#endif
+
+int FOLLY_PYTHON_PyContext_AddWatcher(PyContext_WatchCallback callback) {
+#if defined(__linux__) && FOLLY_PYTHON_META_BUILD
+  return PyContext_AddWatcher(callback);
+#else
+  return 1;
+#endif
+}
+
+} // extern "C"
+
+#else // >= python 3.14
+#define FOLLY_PYTHON_PyContext_AddWatcher PyContext_AddWatcher
+#endif
diff --git a/folly/folly/random/hash.h b/folly/folly/random/hash.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/random/hash.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <utility>
+
+#include <folly/Utility.h>
+#include <folly/functional/Invoke.h>
+
+namespace folly {
+
+/// hash_counter_engine
+///
+/// Hashes an incrementing integer to produce a pseudo-random output.
+///
+/// Pros:
+/// * Keeps minimal state.
+///
+/// Cons:
+/// * Randomness quality depends on the hash function.
+/// * Less performant than a dedicated random engine, even than one based on the
+///   same hash function might be.
+template <typename Hash, typename State>
+class hash_counter_engine : private Hash {
+ private:
+  static auto call(Hash& hash, State state) {
+    if constexpr (is_invocable_v<Hash&, State>) {
+      return hash(state);
+    } else if constexpr (std::is_invocable_v<Hash&, uint8_t const*, size_t>) {
+      auto const ptr = reinterpret_cast<uint8_t const*>(std::addressof(state));
+      return hash(ptr, sizeof(state));
+    } else {
+      static_assert(always_false<Hash, State>);
+    }
+  }
+
+ public:
+  using state_type = State;
+  using result_type =
+      decltype(call(std::declval<Hash&>(), std::declval<state_type>()));
+  static_assert(std::is_integral_v<result_type>);
+  static_assert(!std::is_signed_v<result_type>);
+
+  static constexpr result_type min() { return result_type(); }
+  static constexpr result_type max() { return ~result_type(); }
+
+  explicit hash_counter_engine(state_type state) noexcept : state_{state} {}
+  hash_counter_engine(Hash const& hash, state_type state) noexcept
+      : Hash{hash}, state_{state} {}
+
+  result_type operator()() { return call(*this, state_++); }
+
+ private:
+  state_type state_;
+};
+
+} // namespace folly
diff --git a/folly/folly/random/seed_seq.h b/folly/folly/random/seed_seq.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/random/seed_seq.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstring>
+#include <random>
+#include <type_traits>
+
+#include <folly/Traits.h>
+#include <folly/container/span.h>
+#include <folly/functional/Invoke.h>
+
+namespace folly {
+
+/// seed_seq_generate
+///
+/// Deterministically fills a fixed-size state buffer, represented as a span of
+/// words, with uniformly-distributed data generated from a seed-sequence.
+///
+/// Handles mismatches between the seed-sequence result-type and the word type
+/// of the buffer being filled.
+struct seed_seq_generate_fn {
+ private:
+  FOLLY_CREATE_MEMBER_INVOKER(invoke_generate, generate);
+  using traits = invoke_traits<invoke_generate>;
+  template <typename T, typename R = typename T::result_type>
+  static constexpr bool seq_inv_v = traits::is_invocable_v<T&, R*, R*>;
+  template <typename T, typename R = typename T::result_type>
+  static constexpr bool seq_nx_inv_v =
+      traits::is_nothrow_invocable_v<T&, R*, R*>;
+
+ public:
+  template <
+      typename SeedSeq,
+      typename Word,
+      std::size_t Size,
+      std::enable_if_t<std::is_trivially_copyable_v<Word>, int> = 0,
+      std::enable_if_t<Size != dynamic_extent, int> = 0,
+      std::enable_if_t<seq_inv_v<SeedSeq>, int> = 0>
+  void operator()(span<Word, Size> dst, SeedSeq& seq) const //
+      noexcept(seq_nx_inv_v<SeedSeq>) {
+    using out_t = typename SeedSeq::result_type;
+    constexpr auto dst_bytes = Size * sizeof(Word);
+    constexpr auto out_sz = (dst_bytes + sizeof(out_t) - 1) / sizeof(out_t);
+    out_t out[out_sz];
+    seq.generate(out, out + out_sz);
+    std::memcpy(dst.data(), out, dst_bytes);
+  }
+};
+constexpr inline seed_seq_generate_fn seed_seq_generate{};
+
+/// make_std_seed_seq
+///
+/// Constructs a std::seed_seq seed-sequence with a given seed value.
+///
+/// Handles mismatches between the std::seed_seq result-type and the input value
+/// type.
+struct make_std_seed_seq_fn {
+  template <
+      typename Word,
+      std::enable_if_t<is_non_bool_integral_v<Word>, int> = 0>
+  std::seed_seq operator()(Word word) const {
+    using val_t = std::seed_seq::result_type;
+    constexpr auto vals_sz = (sizeof(Word) + sizeof(val_t) - 1) / sizeof(val_t);
+    val_t vals[vals_sz] = {};
+    std::memcpy(vals, &word, sizeof(Word));
+    return std::seed_seq(vals, vals + vals_sz);
+  }
+};
+constexpr inline make_std_seed_seq_fn make_std_seed_seq{};
+
+} // namespace folly
diff --git a/folly/folly/random/xoshiro256pp.h b/folly/folly/random/xoshiro256pp.h
--- a/folly/folly/random/xoshiro256pp.h
+++ b/folly/folly/random/xoshiro256pp.h
@@ -21,9 +21,13 @@
 #include <limits>
 #include <ostream>
 #include <random>
+#include <utility>
 
 #include <folly/Likely.h>
 #include <folly/Portability.h>
+#include <folly/container/span.h>
+#include <folly/functional/Invoke.h>
+#include <folly/random/seed_seq.h>
 
 #if FOLLY_X64
 #include <immintrin.h>
@@ -31,32 +35,48 @@
 
 namespace folly {
 
-template <typename ResType>
+namespace detail {
+#if defined(__AVX2__) && defined(__GNUC__)
+using DefaultVectorType = __v4du; // GCC-specific unsigned vector type
+#else
+using DefaultVectorType = uint64_t; // Fallback for other compilers
+#endif
+} // namespace detail
+
+using DefaultVectorType = detail::DefaultVectorType;
+
+template <typename ResType, typename VectorType = DefaultVectorType>
 class xoshiro256pp {
+ private:
+  static_assert(sizeof(VectorType) % sizeof(ResType) == 0);
+  static_assert(sizeof(VectorType) >= sizeof(ResType));
+  static_assert(alignof(VectorType) >= alignof(ResType));
+
+  using state_array = VectorType[32];
+  using state_span = decltype(span(std::declval<state_array&>()));
+
+  template <typename SeedSeq>
+  static constexpr bool is_seed_seq =
+      is_invocable_v<seed_seq_generate_fn, state_span, SeedSeq&>;
+
  public:
   using result_type = ResType;
   static constexpr result_type default_seed =
       static_cast<result_type>(0x8690c864c6e0b716);
 
-  // While this is not the actual size of the state, it is the size of the input
-  // seed that we allow. Any uses of a larger state in the form of a seed_seq
-  // will be ignored after the first small part of it.
-  static constexpr size_t state_size = sizeof(uint64_t) / sizeof(result_type);
+  static constexpr size_t state_size =
+      sizeof(state_array) / sizeof(result_type);
   // Add static_asserts to enforce constraints on ResType
   static_assert(
       std::is_integral_v<result_type>, "ResType must be an integral type.");
   static_assert(
       std::is_unsigned_v<result_type>, "ResType must be an unsigned type.");
 
-  xoshiro256pp(uint64_t pSeed = default_seed) noexcept : state{} {
-    seed(pSeed);
-  }
+  xoshiro256pp(result_type value = default_seed) noexcept { seed(value); }
 
-  explicit xoshiro256pp(std::seed_seq& seq) noexcept {
-    // Initialize the state using the seed sequence.
-    uint64_t val;
-    seq.generate(&val, &val + 1);
-    seed(val);
+  template <typename SeedSeq, typename = std::enable_if_t<is_seed_seq<SeedSeq>>>
+  explicit xoshiro256pp(SeedSeq& seq) noexcept {
+    seed(seq);
   }
 
   result_type operator()() noexcept { return next(); }
@@ -67,8 +87,15 @@
     return std::numeric_limits<result_type>::max();
   }
 
-  void seed(uint64_t pSeed = default_seed) noexcept {
-    uint64_t seed = pSeed;
+  /// seed
+  ///
+  /// Deterministically fills the internal state with uniformly-distributed data
+  /// generated from value.
+  ///
+  /// Note: the internal state is larger than result_type, so the entropy of the
+  /// internal state will be smaller than the maximum possible entropy.
+  void seed(result_type value = default_seed) noexcept {
+    uint64_t seed = value;
     for (uint64_t re = 0; re < VecResCount; re++) {
       for (uint64_t stat = 0; stat < StateSize; stat++) {
         state[re][stat] = seed_vec<vector_type>(seed);
@@ -77,19 +104,21 @@
     cur = ResultCount;
   }
 
-  void seed(std::seed_seq& seq) noexcept {
-    // Initialize the state using the seed sequence.
-    std::array<uint64_t, 1> seeds{};
-    seq.generate(seeds.begin(), seeds.end());
-    seed(seeds[0]);
+  /// seed
+  ///
+  /// Deterministically fills the internal state with uniformly-distributed data
+  /// generated from seq.
+  template <typename SeedSeq, std::enable_if_t<is_seed_seq<SeedSeq>, int> = 0>
+  void seed(SeedSeq& seq) {
+    state_array arr;
+    std::memcpy(&arr, &state, sizeof(state_array));
+    seed_seq_generate(span(arr), seq);
+    std::memcpy(&state, &arr, sizeof(state_array));
+    cur = ResultCount;
   }
 
  private:
-#if defined(__AVX2__) && defined(__GNUC__)
-  using vector_type = __v4du; // GCC-specific unsigned vector type
-#else
-  using vector_type = uint64_t; // Fallback for other compilers
-#endif
+  using vector_type = VectorType;
   static constexpr uint64_t StateSize = 4;
   static constexpr uint64_t VecResCount = 8;
   static constexpr uint64_t ResultCount =
@@ -99,18 +128,20 @@
     result_type res[ResultCount];
   };
   vector_type state[VecResCount][StateSize]{};
+  static_assert(sizeof(state) == sizeof(state_array));
   uint64_t cur = ResultCount;
 
-  template <typename Size, typename CharT, typename Traits>
+  template <typename Size, typename VType, typename CharT, typename Traits>
   friend std::basic_ostream<CharT, Traits>& operator<<(
-      std::basic_ostream<CharT, Traits>& os, const xoshiro256pp<Size>& rng);
+      std::basic_ostream<CharT, Traits>& os,
+      const xoshiro256pp<Size, VType>& rng);
 
   template <typename T>
   static inline T seed_vec(uint64_t& seed) {
     if constexpr (sizeof(T) != sizeof(uint64_t)) {
       T sbase{};
       for (uint64_t i = 0; i < sizeof(vector_type) / sizeof(uint64_t); i++) {
-        sbase[i] = splitmix64(seed);
+        sbase[i] = folly::to_narrow(splitmix64(seed));
       }
       return sbase;
     } else {
@@ -153,9 +184,10 @@
   }
 };
 
-template <typename Size, typename CharT, typename Traits>
+template <typename Size, typename VectorType, typename CharT, typename Traits>
 std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os, const xoshiro256pp<Size>& rng) {
+    std::basic_ostream<CharT, Traits>& os,
+    const xoshiro256pp<Size, VectorType>& rng) {
   for (auto i2 : rng.res) {
     os << i2 << " ";
   }
diff --git a/folly/folly/result/coded_rich_error.h b/folly/folly/result/coded_rich_error.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/coded_rich_error.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/result/rich_error.h>
+#include <folly/result/rich_error_base.h>
+#include <folly/result/rich_error_code.h>
+#include <folly/result/rich_exception_ptr.h>
+#include <folly/result/rich_msg.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+/// Start with the user docs in `docs/rich_error_code.md`.
+///
+/// Error type for storing 1 or more application-specific error codes.  Loosely
+/// parallel to `std::system_error` -- though, if you actually have an OS
+/// error, please read `errc_rich_error.h` next.
+///
+/// Compared to `system_error`, this has all the advantages of rich errors --
+/// RTTI-free access on the happy path, automatic source location capture,
+/// cheaper message support, `fmt` and `<<` support, etc.
+///
+/// If you specifically need an analog of `std::nested_exception`, where the
+/// new error shadows the old (partly or completely), then check out the two
+/// templates in `nestable_coded_rich_error.h`.
+template <typename... Codes>
+class coded_rich_error : public rich_error_base {
+ private:
+  template <typename Code>
+  struct code_entry {
+    [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] Code code_value_;
+  };
+
+  template <typename... Cs>
+  struct code_storage : code_entry<Cs>... {};
+
+  code_storage<Codes...> codes_;
+  rich_msg msg_;
+
+ public:
+  /// See `make_coded_rich_error()`, which deduces the code types. This is
+  /// useful as `errc_rich_error::make`, and in similar contexts.
+  ///
+  /// NB: It is fine to add a a variant taking `rich_msg` if needed.
+  template <typename... As>
+  static rich_error<coded_rich_error<Codes...>> make(
+      Codes... codes,
+      ext::format_string_and_location<std::type_identity_t<As>...> snl = "",
+      As const&... args) {
+    return rich_error<coded_rich_error<Codes...>>(
+        std::move(codes)..., rich_msg{std::move(snl), args...});
+  }
+
+  // Private. For ergonomics, prefer `make_coded_rich_error()` or `::make()`
+  // to construct `rich_error<coded_rich_error>{...}`.
+  explicit constexpr coded_rich_error(
+      Codes... codes,
+      // This default is for `immortal_rich_error` usage.
+      rich_msg msg = vtag<literal_string{""}>)
+      : codes_{code_entry<Codes>{codes}...}, msg_{std::move(msg)} {}
+
+  folly::source_location source_location() const noexcept override {
+    return msg_.location();
+  }
+
+  constexpr const char* partial_message() const noexcept override {
+    return msg_.message();
+  }
+
+  /// This will rarely be used by end-users, since `get_rich_error_code<Code>`
+  /// is the canonical accessor for codes.
+  template <typename Code>
+  constexpr const Code& code_of_type() const {
+    return static_cast<const code_entry<Code>&>(codes_).code_value_;
+  }
+
+  /// Easy code access for the common case of "just one code".
+  template <size_t N = sizeof...(Codes)>
+    requires(N == 1)
+  constexpr const auto& code() const {
+    return code_of_type<Codes...>();
+  }
+
+  // `get_rich_error_code` protocol support.
+  using folly_rich_error_codes_t = rich_error_bases_and_own_codes<
+      coded_rich_error,
+      tag_t<>,
+      &coded_rich_error::template code_of_type<Codes>...>;
+  constexpr void retrieve_code(rich_error_code_query& c) const override {
+    return folly_rich_error_codes_t::retrieve_code(*this, c);
+  }
+
+  using folly_get_exception_hint_types = rich_error_hints<coded_rich_error>;
+};
+
+// Creates various `...coded_rich_error` factory functions.
+//
+// `ExtraArgs` specifies additional required arguments between the code(s) and
+// message. For example, nestable errors require `rich_exception_ptr`.
+template <template <typename...> class ErrorT, typename... ExtraArgs>
+struct make_coded_rich_error_fn {
+  // Common case -- single code:
+  //   ErrorT(MyCode::VAL, [extra_args,] "info={}", info)
+  template <typename Code, typename... As>
+  constexpr rich_error<ErrorT<Code>> operator()(
+      Code code,
+      ExtraArgs... extra_args,
+      ext::format_string_and_location<std::type_identity_t<As>...> snl = "",
+      As const&... args) const {
+    return rich_error<ErrorT<Code>>{
+        std::move(code),
+        std::move(extra_args)...,
+        rich_msg{std::move(snl), args...}};
+  }
+  // Power users -- multiple codes:
+  //   ErrorT(rich_msg{"msg"}, [extra_args,] Code1::A, Code2::B)
+  template <typename... Codes>
+  constexpr rich_error<ErrorT<Codes...>> operator()(
+      rich_msg msg, ExtraArgs... extra_args, Codes... codes) const {
+    return rich_error<ErrorT<Codes...>>{
+        std::move(codes)..., std::move(extra_args)..., std::move(msg)};
+  }
+};
+
+/// Make `rich_error<coded_rich_error<Codes...>>` with deduced code types.
+///
+/// Common case -- single code:
+///   make_coded_rich_error(MyCode::VAL, "more={}, info={}", more, info)
+/// Power users -- multiple codes:
+///   make_coded_rich_error(rich_msg{"a={}", a}, Code1::A, Code2::B)
+inline constexpr make_coded_rich_error_fn<coded_rich_error>
+    make_coded_rich_error{};
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/coro.h b/folly/folly/result/coro.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/coro.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/CppAttributes.h>
+#include <folly/coro/Coroutine.h> // delete with clang-15 support
+#include <folly/coro/Error.h>
+#include <folly/lang/MustUseImmediately.h>
+#include <folly/result/detail/result_or_unwind.h>
+#include <folly/result/detail/result_promise.h>
+#include <folly/result/result.h>
+
+#if FOLLY_HAS_RESULT
+
+// One must never conditionally-include folly headers, but this is required to
+// be gated, since `result.h` may get included in C++17 contexts.
+#include <coroutine>
+
+/// co_await or_unwind(resultFunc())
+///
+/// Recommended pre-read: `result.h` docblock and/or `docs/result.md`.
+///
+/// Use `or_unwind` to unwrap short-circuiting types from synchronous `result`
+/// coroutines OR from `folly::coro` async task coroutines:
+///
+///   result<int> getN();
+///   int n = co_await or_unwind(getN());
+///
+/// Supported types: `result<T>`, `value_only_result<T>`, `error_or_stopped`.
+/// Reference variants (`&`, `const&`, `&&`) bind to the argument; use
+/// `or_unwind_owning` when you need to store or return the awaitable.
+///
+/// If you JUST need a task to cheaply propagate exceptions to its parent,
+/// avoiding rethrowing, then you have a more concise option:
+///
+///   auto v = co_await co_nothrow(asyncMayError()); // best practice
+///   // equivalent, but too long
+///   auto v = co_await or_unwind(
+///       co_await value_or_error_or_stopped(asyncMayError()));
+///
+/// However, when you are calling synchronous `result` functions, or need to
+/// efficiently handle **some** async errors, `or_unwind` is your friend:
+///
+///   // Or: `res = co_await value_or_error_or_stopped(asyncFn());`
+///   auto res = syncResultFn();
+///    if (auto ex = get_exception<MyError>(res)) {
+///     // handle ex, which quacks like `const MyError*`
+///   } else {
+///     auto v = co_await or_unwind(std::move(res)); // propagate unhandled
+///   }
+///
+/// This pattern has a few good properties:
+///   - Easy error handling -- extracts the value from its argument, or
+///     short-circuit any error to current coro's awaiter.
+///   - Unlike `catch (const std::exception& ex)`, won't catch (and therefore
+///     break) cancellation.
+///   - The error path is MUCH more efficient (3-30 nanoseconds) than
+///     `value_or_throw()` (1 microsecond).
+///
+/// ## Avoid accidental copies
+///
+/// Note that if you merely wrote `or_unwind(res)` above, you would be
+/// unnecessarily copying the value OR the error (7ns), so mind your move
+/// hygiene in hot code.
+///
+/// ## Avoid dangling references with `or_unwind(rvalue)`
+///
+/// WARNING: `auto&& ref = co_await or_unwind(resFn())` dangles! `or_unwind`
+/// stores a reference to its argument, so when `resFn()` returns a temporary,
+/// it dies after the full expression. Use `auto val = ...` (store by value),
+/// or `or_unwind_owning`. Search `result.md` for "LLVM issue #177023" for more.
+///
+/// ## What about cancellation / `has_stopped()`?
+///
+/// `co_await or_unwind(resFn())` only extracts the value, and unwinds to the
+/// parent on either "error" or "stopped" outcomes.
+///
+/// ## Why not `co_await resultFn()`?
+///
+/// Making `result` implicitly awaitable would guarantee confusion between
+/// async-awaits, and synchronous propagation of error-or-stopped outcomes.  The
+/// distinction is critical.  For example, one must not hold non-coro mutexes
+/// across async suspend points.
+///
+/// ## Future: Extensions to `Try`?
+///
+/// While, per `design_notes.md`, new code will benefit from using `result` over
+/// `Try`, it might be fine to specialize `or_unwind` for `Try`.  Just be
+/// mindful of its two warts: empty state and empty `exception_wrapper`.
+
+// NOTE: This short-circuiting coroutine implementation was modeled on
+// `folly/Expected.h`.  Port compiler fixes or optimizations across both.
+
+namespace folly {
+
+// NB: Making these `final` makes `unsafe_mover` simpler due to no slicing risk.
+
+/// `co_await or_unwind(resFn())` returns a reference into `res`.
+template <typename T>
+class [[nodiscard]] or_unwind<result<T>&&> final
+    : public detail::result_or_unwind<result<T>&&> {
+  using detail::result_or_unwind<result<T>&&>::result_or_unwind;
+};
+template <typename T>
+or_unwind(result<T>&&) -> or_unwind<result<T>&&>;
+
+/// `co_await or_unwind(res)` returns a reference into `res`.
+template <typename T>
+class [[nodiscard]] or_unwind<result<T>&> final
+    : public detail::result_or_unwind<result<T>&> {
+  using detail::result_or_unwind<result<T>&>::result_or_unwind;
+};
+template <typename T>
+or_unwind(result<T>&) -> or_unwind<result<T>&>;
+
+/// `co_await or_unwind(std::as_const(res))` returns a reference into `res`.
+template <typename T>
+class [[nodiscard]] or_unwind<const result<T>&> final
+    : public detail::result_or_unwind<const result<T>&> {
+  using detail::result_or_unwind<const result<T>&>::result_or_unwind;
+};
+template <typename T>
+or_unwind(const result<T>&) -> or_unwind<const result<T>&>;
+
+/// `co_await or_unwind_owning(res)` takes ownership of `res`.
+///
+/// Use when you need the awaitable to own the result (e.g., when returning
+/// from a helper function that composes awaitables).
+template <typename T>
+class [[nodiscard]] or_unwind_owning<result<T>> final
+    : public detail::result_or_unwind_owning<result<T>> {
+  using detail::result_or_unwind_owning<result<T>>::result_or_unwind_owning;
+};
+template <typename T>
+or_unwind_owning(result<T>) -> or_unwind_owning<result<T>>;
+
+/// `co_await or_unwind(error_or_stopped{...})` terminates the current scope
+///
+/// IMPORTANT: Unlike `result<T>`, there's no lvalue `or_unwind(eos&)` variant.
+/// If adding one, and add a test akin to the `result<T>` lvalue mutation check
+/// in `forEachOrUnwindVariant`.
+template <>
+class [[nodiscard]] or_unwind<error_or_stopped&&> final
+    : public detail::result_or_unwind<error_or_stopped&&> {
+  using detail::result_or_unwind<error_or_stopped&&>::result_or_unwind;
+};
+or_unwind(error_or_stopped&&) -> or_unwind<error_or_stopped&&>;
+
+/// co_await or_unwind_owning(error_or_stopped{...})
+/// co_await or_unwind_owning(stopped_result)
+/// co_await or_unwind(stopped_result)
+///
+/// Owning awaitable for `error_or_stopped`. Unlike the reference variant,
+/// this can be returned from functions or stored before awaiting.
+template <>
+class [[nodiscard]] or_unwind_owning<error_or_stopped> final
+    : public detail::result_or_unwind_owning<error_or_stopped> {
+  using detail::result_or_unwind_owning<
+      error_or_stopped>::result_or_unwind_owning;
+};
+or_unwind_owning(error_or_stopped) -> or_unwind_owning<error_or_stopped>;
+or_unwind_owning(stopped_result_t) -> or_unwind_owning<error_or_stopped>;
+
+// `or_unwind(stopped_result)` must create an owning variant since
+// `stopped_result_t` is a tag type, not a reference we can store.
+//
+// Implementation edge case: Yes, `or_unwind<error_or_stopped>` is owning,
+// despite the lack of `_owning` in the name.  This works out because
+// `result_or_unwind_base` selects the storage base using the inner type.
+template <>
+class [[nodiscard]] or_unwind<error_or_stopped> final
+    : public detail::result_or_unwind<error_or_stopped> {
+  using detail::result_or_unwind<error_or_stopped>::result_or_unwind;
+};
+or_unwind(stopped_result_t) -> or_unwind<error_or_stopped>;
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/detail/immortal_exception_storage.h b/folly/folly/result/detail/immortal_exception_storage.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/detail/immortal_exception_storage.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/result/rich_error_fwd.h>
+
+#include <exception>
+#include <typeinfo>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly::detail {
+
+// `rich_exception_ptr`'s type-erased interface for immortal exceptions
+class immortal_exception_storage {
+ private:
+  template <typename, typename>
+  friend class rich_exception_ptr_impl;
+
+  // Address of the immortal error instance -- not fully type-erased to allow
+  // constexpr access to `rich_error_base` APIs (like codes).
+  const rich_error_base* immortal_ptr_;
+
+  const std::type_info* user_base_type_;
+  const std::type_info* rich_error_leaf_type_;
+
+  // Potentially cheaper than `std::rethrow_exception(to_exception_ptr())`
+  // IMPORTANT: Mark any derived function `[[noreturn]]`, we assume this.
+  virtual void throw_exception() const = 0;
+
+  // Immutable access to a singleton that, unlike `to_exception_ptr()`,
+  // permanently contains an unchanged copy of `*immortal_ptr_`.
+  virtual const rich_error_base* as_immutable_leaf_rich_error() const = 0;
+  virtual const std::exception* as_immutable_std_exception() const = 0;
+
+  // Mutable access to the exception singleton from `to_exception_ptr()`.
+  //
+  // Must be `const`-qualified because `immortal_exception_storage` instances
+  // are necessarily `constexpr`, but it is not LOGICALLY `const`.
+  virtual rich_error_base* as_mutable_leaf_rich_error() const = 0;
+  virtual std::exception* as_mutable_std_exception() const = 0;
+  virtual std::exception_ptr* acquire_mutable_singleton_ptr_if_already_created()
+      const = 0;
+
+  // Undo type erasure: Returns a copy of a singleton `std::exception_ptr` of a
+  // `rich_error<>`, which contains a copy of `*immortal_ptr_`.  Unlike the
+  // `as_immutable_...` singleton, this copy CAN change.
+  //
+  // Note: Immortal exceptions are NEVER empty, by construction.
+  virtual std::exception_ptr to_exception_ptr() const = 0;
+
+ protected:
+  // `consteval` guarantees the incoming pointer is immortal.
+  consteval immortal_exception_storage(
+      const rich_error_base* p,
+      const std::type_info* me,
+      const std::type_info* leaf)
+      : immortal_ptr_{p}, user_base_type_{me}, rich_error_leaf_type_{leaf} {}
+
+ public:
+  constexpr virtual ~immortal_exception_storage() = default;
+};
+
+} // namespace folly::detail
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/detail/ptr_immortal_exception_storage.h b/folly/folly/result/detail/ptr_immortal_exception_storage.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/detail/ptr_immortal_exception_storage.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <exception>
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/Traits.h>
+#include <folly/lang/SafeAssert.h>
+#include <folly/result/rich_exception_ptr.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly::detail {
+
+template <const auto* P>
+class ptr_immortal_exception_storage : immortal_exception_storage {
+ private:
+  // Map `const immortal_rich_error_storage<B>*` -> `B`
+  using UserBase = std::remove_const_t<std::remove_pointer_t<decltype(P)>>::
+      folly_private_immortal_rich_error_base;
+  using LeafRichError = ::folly::rich_error<UserBase>;
+
+  LeafRichError* immutable_leaf_rich_error_singleton() const {
+    // Union trick: the empty destructor ensures `value` is never destroyed.
+    // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
+    union Storage {
+      LeafRichError value;
+      Storage() : value{immortal_rich_error_private_t{}, *P} {}
+      ~Storage() {}
+    };
+    // Meyer singleton is thread-safe past C++11. Leak it to avoid SDOF.
+    //
+    // Yes, this will make an instance per DSO. That's expected and OK.
+    // NOLINTNEXTLINE(facebook-hte-InlinedStaticLocalVariableWarning)
+    static Storage storage;
+    return &storage.value;
+  }
+
+  // In contrast to the immutable singleton, `rich_exception_ptr::operator==`
+  // must be able to peek at the singleton **without** creating it -- doing so
+  // would waste time & memory in programs trying to efficiently use immortals.
+  // So, we can't use leaky Meyer here.
+  class mutable_std_exception_leaf_rich_error_singleton {
+   private:
+    // Aligned byte storage for `std::exception_ptr` instead of an actual
+    // `std::exception_ptr` (that would make us non-constant-initializable).
+    //
+    // Constructed via `ensure_created()` and leaked / immortal thereafter.
+    alignas(std::exception_ptr) mutable char eptr_buf_[sizeof(
+        std::exception_ptr)]{};
+    // Initially null. After `ensure_created()`, points to `eptr_buf_`.
+    mutable std::atomic<std::exception_ptr*> eptr_{nullptr};
+    mutable std::mutex ensure_created_mutex_{};
+
+    // Cold path of `get()`
+    std::exception_ptr* ensure_created() const {
+      std::lock_guard lock(ensure_created_mutex_);
+      // Relaxed load ok -- the mutex synchronizes us with competing creators.
+      auto* eptr = eptr_.load(std::memory_order_relaxed);
+      if (FOLLY_LIKELY(!eptr)) {
+        eptr = new (eptr_buf_) std::exception_ptr{make_exception_ptr_with(
+            std::in_place_type<LeafRichError>,
+            immortal_rich_error_private_t{},
+            *P)};
+        // Synchronizes with the load-acquires in `get()` and
+        // `acquire_ptr_if_already_created()`.
+        eptr_.store(eptr, std::memory_order_release);
+      }
+      FOLLY_SAFE_DCHECK(eptr != nullptr);
+      return eptr;
+    }
+
+   public:
+    std::exception_ptr& get() const {
+      auto* eptr = eptr_.load(std::memory_order_acquire);
+      if (FOLLY_UNLIKELY(!eptr)) {
+        eptr = ensure_created();
+      }
+      FOLLY_SAFE_DCHECK(eptr != nullptr);
+      return *eptr;
+    }
+
+    // Used by `operator==` to avoid unnecessarily creating the eptr
+    std::exception_ptr* acquire_ptr_if_already_created() const {
+      return eptr_.load(std::memory_order_acquire);
+    }
+  };
+  constexpr static mutable_std_exception_leaf_rich_error_singleton
+      mutable_eptr_singleton_{};
+
+  template <typename Ex>
+  Ex* get_mutable_exception_impl() const {
+    // The `LeafRichError` cast is safe, see `ensure_created()` above.
+    return static_cast<Ex*>(static_cast<LeafRichError*>(
+        exception_ptr_get_object(mutable_eptr_singleton_.get())));
+  }
+
+ protected:
+  template <typename, typename, auto...>
+  friend class folly::immortal_rich_error_t;
+  consteval ptr_immortal_exception_storage()
+      : immortal_exception_storage{
+            P, &typeid(UserBase), &typeid(LeafRichError)} {}
+
+  [[noreturn]] void throw_exception() const override {
+    throw *immutable_leaf_rich_error_singleton();
+  }
+
+  // Since `std::exception` isn't `constexpr` before C++26, these two have to
+  // be backed by an immutable leaky singleton.
+  const rich_error_base* as_immutable_leaf_rich_error() const override {
+    return immutable_leaf_rich_error_singleton();
+  }
+  const std::exception* as_immutable_std_exception() const override {
+    return immutable_leaf_rich_error_singleton();
+  }
+
+  // `to_exception_ptr()` and `folly::get_mutable_exception()` give mutable
+  // access to an exception object.  A mutable singleton backs both.
+  rich_error_base* as_mutable_leaf_rich_error() const override {
+    return get_mutable_exception_impl<LeafRichError>();
+  }
+  std::exception* as_mutable_std_exception() const override {
+    return get_mutable_exception_impl<std::exception>();
+  }
+  std::exception_ptr* acquire_mutable_singleton_ptr_if_already_created()
+      const override {
+    return mutable_eptr_singleton_.acquire_ptr_if_already_created();
+  }
+  std::exception_ptr to_exception_ptr() const override {
+    // Future: This can avoid atomic refcount ops on __GLIBCXX__ and
+    // _LIBCPP_VERSION build flavors, as follows.
+    //   - Add new detail helpers to `lang/Exception.cpp`, one to make an
+    //     immortal `std::exception_ptr` with a HUGE refcount, and the other to
+    //     cheaply copy it without bumping the refcount.  See `referenceCount`
+    //     in `make_exception_ptr_with_()`.
+    //   - For example, the initial refcount can be 2^63, and the copy
+    //     method only adds 2^63 when the count drops below 2^60.
+    //   - Use the helpers to construct / copy the eptr here.
+    return mutable_eptr_singleton_.get();
+  }
+};
+
+} // namespace folly::detail
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/detail/result_or_unwind.h b/folly/folly/result/detail/result_or_unwind.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/detail/result_or_unwind.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/CppAttributes.h>
+#include <folly/coro/Error.h>
+#include <folly/lang/MustUseImmediately.h>
+#include <folly/result/detail/result_promise.h>
+#include <folly/result/result.h>
+
+#if FOLLY_HAS_RESULT
+
+#include <coroutine>
+
+namespace folly {
+
+template <typename>
+class or_unwind;
+
+template <typename>
+class or_unwind_owning;
+
+class Executor;
+template <typename>
+class ExecutorKeepAlive;
+
+namespace coro::detail {
+struct WithAsyncStackFunction;
+class TaskPromisePrivate;
+} // namespace coro::detail
+
+namespace detail {
+
+// Base class for non-owning `or_unwind` types that store refs.
+//
+// Lifetime safety: Storing a reference to a temporary would dangle, but we're
+// protected by `must_use_immediately_crtp` (must be awaited in the same
+// full-expression) and C++ temporary lifetime extension for the prvalue case.
+// The `unsafe_mover` protocol lets `co_viaIfAsync` etc. "move" these types
+// despite being must-use-immediately.
+//
+// KNOWN BUG (LLVM issue #177023): `auto&& ref = co_await or_unwind(rval())`
+// dangles because `co_await` hides the lifetime chain from Clang's
+// `lifetimebound` analysis. The annotations below DO work for non-coroutine
+// code but NOT through `co_await`. See `result.md` for safe alternatives.
+template <typename Derived, typename StorageRef>
+class result_or_unwind_ref_base
+    : public ext::must_use_immediately_crtp<Derived> {
+  static_assert(std::is_reference_v<StorageRef>);
+  StorageRef storage_;
+
+ protected:
+  static constexpr bool kIsErrorOrStopped =
+      std::is_same_v<std::remove_cvref_t<StorageRef>, error_or_stopped>;
+
+ private:
+  struct my_mover {
+    StorageRef storage_;
+    explicit my_mover(StorageRef&& s) noexcept
+        : storage_(static_cast<StorageRef&&>(s)) {}
+    Derived operator()() && noexcept {
+      return Derived{static_cast<StorageRef&&>(storage_)};
+    }
+  };
+
+ protected:
+  StorageRef&& storage() && noexcept {
+    return static_cast<StorageRef&&>(storage_);
+  }
+  const auto& storage() const& noexcept { return storage_; }
+
+ public:
+  explicit result_or_unwind_ref_base(
+      StorageRef&& r [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]]) noexcept
+      : storage_(static_cast<StorageRef&&>(r)) {}
+
+  [[nodiscard]] decltype(auto) await_resume() noexcept
+      [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] {
+    if constexpr (kIsErrorOrStopped) {
+      // `await_ready()` is false, the coro gets destroyed in `await_suspend()`
+      compiler_may_unsafely_assume_unreachable();
+    } else {
+      // Result type: return a reference into the stored result.
+      return static_cast<StorageRef&&>(storage_).value_or_throw();
+    }
+  }
+
+  // unsafe_mover protocol for must-use-immediately types
+  static my_mover unsafe_mover(
+      ext::must_use_immediately_private_t, Derived&& me) noexcept {
+    return my_mover{static_cast<StorageRef&&>(me.storage_)};
+  }
+};
+
+// Base class for `or_unwind_owning` types that own the result-like values.
+// Unlike the ref base, these are movable types (no `must_use_immediately` or
+// `unsafe_mover`).  `await_resume` returns by value to avoid dangling.
+template <typename Derived, typename StorageVal>
+class result_or_unwind_value_base {
+  static_assert(!std::is_reference_v<StorageVal>);
+  StorageVal storage_;
+
+ protected:
+  static constexpr bool kIsErrorOrStopped =
+      std::is_same_v<StorageVal, error_or_stopped>;
+
+  StorageVal&& storage() && noexcept { return std::move(storage_); }
+  const StorageVal& storage() const& noexcept { return storage_; }
+
+ public:
+  explicit result_or_unwind_value_base(StorageVal s) noexcept
+      : storage_(std::move(s)) {
+    // It is unexpected for `or_unwind` to throw, so ban this usage until we
+    // encounter a use-case requiring more nuance.
+    static_assert(std::is_nothrow_move_constructible_v<StorageVal>);
+  }
+
+  explicit result_or_unwind_value_base(stopped_result_t s) noexcept
+      // Can remove -- this just communicates that `or_unwind{stopped_result}
+      // constructs `or_unwind<error_or_stopped>`, not `or_unwind<result<T>>`.
+    requires kIsErrorOrStopped
+      : storage_(s) {}
+
+  [[nodiscard]] decltype(auto) await_resume() noexcept {
+    if constexpr (kIsErrorOrStopped) {
+      // `await_ready()` is false, the coro gets destroyed in `await_suspend()`
+      compiler_may_unsafely_assume_unreachable();
+    } else if constexpr (std::is_reference_v<typename StorageVal::value_type>) {
+      return std::move(storage_).value_or_throw(); // Return ref into result.
+    } else {
+      // Owning result types: return by value to avoid dangling refs.
+      return
+          typename StorageVal::value_type(std::move(storage_).value_or_throw());
+    }
+  }
+};
+
+// Helper to select ref vs value base class.
+template <typename Derived, typename Storage>
+using result_or_unwind_base = std::conditional_t<
+    std::is_reference_v<Storage>,
+    result_or_unwind_ref_base<Derived, Storage>,
+    result_or_unwind_value_base<Derived, Storage>>;
+
+// Shared awaitable logic for both ref and value storage.
+template <typename Derived, typename Storage>
+class result_or_unwind_crtp : public result_or_unwind_base<Derived, Storage> {
+  using Base = result_or_unwind_base<Derived, Storage>;
+
+  // `result<T>` has `error_or_stopped()`, `value_only_result` does not
+  static constexpr bool kHasErrorOrStopped = requires {
+    FOLLY_DECLVAL(Storage&&).error_or_stopped();
+  };
+
+ public:
+  using Base::await_resume;
+  using Base::Base;
+
+  [[nodiscard]] bool await_ready() const noexcept {
+    if constexpr (Base::kIsErrorOrStopped) {
+      return false;
+    } else {
+      return this->storage().has_value();
+    }
+  }
+
+  // Suspend into a `result<U>` coroutine.
+  template <typename U>
+  void await_suspend(result_promise_handle<U> h) noexcept {
+    auto toResult = [&](auto&& eos) {
+      auto& v = *h.promise().value_;
+      expected_detail::ExpectedHelper::assume_empty(v.exp_);
+      // For lvalue `Storage` (e.g. `result<T>&`), `storage()` returns a ref,
+      // and `result::error_or_stopped() const&` returns `const
+      // error_or_stopped&`, which gets copied into `Unexpected` -- preserving
+      // the original result. This is intentional: users don't expect `co_await
+      // or_unwind(r)` to mutate `r`, since `r` might outlive the current coro.
+      v.exp_ = Unexpected{static_cast<decltype(eos)>(eos)};
+      h.destroy(); // Abort the rest of the coroutine, resume() won't be called
+    };
+    if constexpr (Base::kIsErrorOrStopped) {
+      toResult(std::move(*this).storage());
+    } else if constexpr (kHasErrorOrStopped) {
+      toResult(std::move(*this).storage().error_or_stopped());
+    } else {
+      // `value_only_result`: not reached since `await_ready()` is always true.
+      compiler_may_unsafely_assume_unreachable();
+    }
+  }
+
+  // Suspend into a task-like coroutine (`now_task`, `Task`, etc.).
+  //
+  // NB: Do NOT support `AsyncGenerator` which also has `co_yield co_error`.
+  // It doesn't abort the generator, a surprising semantic for `or_unwind`.
+  template <typename Promise>
+    requires requires(Promise p, coro::detail::TaskPromisePrivate priv) {
+      p.continuationRef(priv);
+    }
+  auto await_suspend(std::coroutine_handle<Promise> h) noexcept {
+    // Uses the legacy `co_error` API because `folly::coro` models cancellation
+    // as an exception, and checking for `OperationCancelled` costs 50-100ns+.
+    auto toTask = [&](error_or_stopped&& eos) {
+      return h.promise()
+          .yield_value(
+              coro::co_error(
+                  std::move(eos).get_legacy_error_or_cancellation_slow(
+                      result_private_t{})))
+          .await_suspend(h);
+    };
+    if constexpr (Base::kIsErrorOrStopped) {
+      return toTask(std::move(*this).storage());
+    } else if constexpr (kHasErrorOrStopped) {
+      // `copy` since `get_legacy_error_or_cancellation_slow` lacks `const&`.
+      return toTask(
+          ::folly::copy(std::move(*this).storage().error_or_stopped()));
+    } else {
+      // `value_only_result`: not reached since `await_ready()` is always true.
+      compiler_may_unsafely_assume_unreachable();
+    }
+  }
+
+  // Bypass `co_viaIfAsync` / `withAsyncStack`: `or_unwind` is synchronous.
+  friend auto co_viaIfAsync(
+      const ExecutorKeepAlive<Executor>&, Derived r) noexcept {
+    return ext::must_use_immediately_unsafe_mover(std::move(r))();
+  }
+  // Conventionally, the first arg would be `cpo_t<co_withAsyncStack>`, but
+  // that cannot be forward-declared.
+  friend auto tag_invoke(
+      const coro::detail::WithAsyncStackFunction&, Derived&& r) noexcept {
+    return ext::must_use_immediately_unsafe_mover(std::move(r))();
+  }
+};
+
+// Aliases to reduce CRTP boilerplate in `or_unwind` specializations.
+template <typename R>
+using result_or_unwind = result_or_unwind_crtp<::folly::or_unwind<R>, R>;
+template <typename R>
+using result_or_unwind_owning =
+    result_or_unwind_crtp<::folly::or_unwind_owning<R>, R>;
+
+} // namespace detail
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/detail/result_promise.h b/folly/folly/result/detail/result_promise.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/detail/result_promise.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/coro/Coroutine.h> // delete with clang-15 support
+#include <folly/result/result.h>
+
+#if FOLLY_HAS_RESULT
+
+#include <coroutine>
+
+namespace folly {
+namespace detail {
+
+template <typename>
+struct result_promise_base;
+
+template <typename T>
+struct result_promise_return {
+  result<T> storage_{expected_detail::EmptyTag{}};
+  result<T>*& pointer_;
+
+  /* implicit */ result_promise_return(result_promise_base<T>& p) noexcept
+      : pointer_{p.value_} {
+    pointer_ = &storage_;
+  }
+  result_promise_return(result_promise_return const&) = delete;
+  void operator=(result_promise_return const&) = delete;
+  result_promise_return(result_promise_return&&) = delete;
+  void operator=(result_promise_return&&) = delete;
+  // Remove this once clang 15 is well-forgotten. From D42260201:
+  // letting dtor be trivial makes the coroutine crash
+  ~result_promise_return() {}
+
+  /* implicit */ operator result<T>() {
+    // Simplify this once clang 15 is well-forgotten, and remove the dep on
+    // `Coroutine.h`.  From D42260201: handle both deferred and eager
+    // return-object conversion behaviors see docs for
+    // detect_promise_return_object_eager_conversion
+    if (coro::detect_promise_return_object_eager_conversion()) {
+      assert(storage_.is_expected_empty());
+      return result<T>{expected_detail::EmptyTag{}, pointer_}; // eager
+    } else {
+      assert(!storage_.is_expected_empty());
+      return std::move(storage_); // deferred
+    }
+  }
+};
+
+template <typename T>
+struct result_promise_base {
+  result<T>* value_ = nullptr;
+
+  result_promise_base() = default;
+  result_promise_base(result_promise_base const&) = delete;
+  void operator=(result_promise_base const&) = delete;
+  result_promise_base(result_promise_base&&) = delete;
+  void operator=(result_promise_base&&) = delete;
+  ~result_promise_base() = default;
+
+  std::suspend_never initial_suspend() const noexcept { return {}; }
+  std::suspend_never final_suspend() const noexcept { return {}; }
+  void unhandled_exception() noexcept {
+    *value_ = error_or_stopped::from_current_exception();
+  }
+
+  result_promise_return<T> get_return_object() noexcept { return *this; }
+};
+
+template <typename T>
+struct result_promise<T, typename std::enable_if<!std::is_void_v<T>>::type>
+    : public result_promise_base<T> {
+  // For reference types, this deliberately requires users to `co_return`
+  // one of `std::ref`, `std::cref`, or `folly::rref`.
+  //
+  // The default for `U` is tested in `returnImplicitCtor`.
+  template <typename U = T>
+  void return_value(U&& u) {
+    auto& v = *this->value_;
+    expected_detail::ExpectedHelper::assume_empty(v.exp_);
+    v = static_cast<U&&>(u);
+  }
+};
+
+template <typename T>
+struct result_promise<T, typename std::enable_if<std::is_void_v<T>>::type>
+    : public result_promise_base<T> {
+  // You can fail via `co_await err` since void coros only allow `co_return;`.
+  void return_void() { this->value_->exp_.emplace(unit); }
+};
+
+template <typename T> // Need an alias since nested types cannot be deduced.
+using result_promise_handle = std::coroutine_handle<result_promise<T>>;
+
+} // namespace detail
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/detail/rich_error_common.h b/folly/folly/result/detail/rich_error_common.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/detail/rich_error_common.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/Traits.h>
+#include <folly/lang/Pretty.h>
+#include <folly/result/rich_error_base.h>
+
+// Shared details for `rich_error.h` and `immortal_rich_error.h`.
+
+#if FOLLY_HAS_RESULT
+
+namespace folly::detail {
+
+template <typename T>
+struct rich_error_add_partial_message_impl : public T {
+  using T::T;
+  constexpr const char* partial_message() const noexcept override {
+    return pretty_name<T>();
+  }
+};
+
+// Non-abstract only if `T` implements `partial_message()`
+template <typename T>
+class rich_error_test_for_partial_message : T {
+ private:
+  rich_error_test_for_partial_message() = delete; // don't construct or inherit
+  void only_rich_error_may_instantiate(
+      rich_error_base::only_rich_error_may_instantiate_t) override {}
+};
+
+// Adds `partial_message() only if `T` doesn't provide it.
+//
+// Design rationale: There are two reasonable ideas for having `rich_error<>`
+// and `immortal_rich_error<>` automatically add in a simple `partial_message`
+// if it's not already supplied:
+//
+//   - The current one.  It has the downside of making the class hierarchy
+//     deeper for errors that don't implement their own.  This in turn can slow
+//     down `dynamic_cast` (when our RTTI-free optimizations don't apply, see
+//     `rich_exception_ptr_bench.cpp`).  However, in practice, production
+//     errors should likely define a better message anyhow, so this is fine.
+//
+//   - Have the leaf classes always define `partial_message()`, and internally
+//     delegate via `if constexpr ()` to the base if it has one.  This one is
+//     also ok, but it adds code size for ALL error types, unconditionally.
+template <typename T>
+using rich_error_with_partial_message = conditional_t<
+    !std::is_abstract_v<rich_error_test_for_partial_message<T>>,
+    T,
+    rich_error_add_partial_message_impl<T>>;
+
+// Validate a user-defined "rich error" class `Ex` while instantiating
+// `rich_error<Ex>` or `immortal_rich_error<Ex>`.
+//
+// Thanks to the `rich_error_base` passkey, a user should not be able to
+// instantiate such an `Ex` directly, so this provides pretty good guarantees
+// that `Ex*` can only be obtained from `...rich_error<Ex>`, and thus has
+// passed these validations.
+//
+// Why isn't this a concept contraining class templates? Two reasons:
+//   - A constraint prevents referencing `rich_error<A>` from `A`.
+//   - Forward-declarations get messier.
+//
+// Future: Most user types for `rich_error` should **NOT** inherit from
+// `std::exception`, since the leaf wrappers will add it to the dynamic type.
+// But, we cannot uniformly enforce this since there are valid uses that do
+// need it on the base.  If the issue of "accidentally adding an
+// `std::exception` diamond" is both common and problematic, we could either:
+//   - Change `rich_error` to only add it if it's not already there.
+//   - Ban it by default and add an API to bypass that check.
+//
+// `require_sizeof` since some checks are UB with incomplete types
+template <
+    typename UserEx,
+    std::derived_from<UserEx> ActualEx, // rich_error<> or immortal...storage<>
+    size_t = require_sizeof<UserEx>>
+inline constexpr bool static_assert_is_valid_rich_error_type(const ActualEx*) {
+  static_assert(
+      std::derived_from<UserEx, rich_error_base>,
+      "Rich error types must derive from `rich_error_base`.");
+  // Without the offset-0 property, it would be UB to do the RTTI-free
+  // `rich_error_base` access, as implemented by `rich_exception_ptr`.
+  static_assert(
+      detail::has_offset0_base<ActualEx, rich_error_base>,
+      "When inheriting from `rich_error_base`, that type (or its derived type) "
+      "must be first in the inheritance list.");
+  // Did the user-provided type correctly use `rich_error_hints`?
+  //
+  // Future: If you have a strong case for NOT hinting a base error class in
+  // its own definition, add a bypass for the "each type must hint at least
+  // itself" rich error static assert.  The bypass can simply be a
+  // `folly_`-prefixed member type alias that points to the current class, e.g.
+  //   struct UnhintedBase : rich_error_base {
+  //     using folly_rich_error_do_not_require_hint_for = UnhintedBase;
+  //   };
+  using Hint = typename UserEx::folly_get_exception_hint_types;
+  static_assert(
+      type_list_find_v<rich_error<UserEx>, Hint> < type_list_size_v<Hint>,
+      "A rich error user type `T` should use `rich_error_hints<T>` to avoid "
+      "RTTI costs. If this is a base class, read the 'hints' docblock.");
+  return true;
+}
+
+} // namespace folly::detail
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/detail/rich_exception_ptr_storage.cpp b/folly/folly/result/detail/rich_exception_ptr_storage.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/detail/rich_exception_ptr_storage.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/result/detail/rich_exception_ptr_storage.h>
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+
+#include <glog/logging.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly::detail {
+
+#ifndef NDEBUG
+void rich_exception_ptr_base_storage::debug_assert(const char* msg, bool cond) {
+  DCHECK(cond) << msg;
+}
+#endif
+
+// Analog of `exception_wrapper::onNoExceptionError`
+[[noreturn]] void
+rich_exception_ptr_base_storage::terminate_on_empty_or_invalid_eptr(bits_t b) {
+  LOG(FATAL) << "Cannot use `throw_exception()` with an empty or invalid "
+             << "`folly::rich_exception_ptr` (bits " << b << ")." << std::endl;
+}
+
+} // namespace folly::detail
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/detail/rich_exception_ptr_storage.h b/folly/folly/result/detail/rich_exception_ptr_storage.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/detail/rich_exception_ptr_storage.h
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/Traits.h> // vtag_t
+#include <folly/result/detail/immortal_exception_storage.h>
+
+#include <cstdint>
+#include <new>
+
+#if FOLLY_HAS_RESULT
+
+// These are implementation details of `rich_exception_ptr`, used via private
+// inheritance to enforce that users don't have to look at this file to find
+// its public interface.
+
+namespace folly::detail {
+
+// Internal-only sigil types for `rich_exception_ptr` union states.
+//
+// FOLLY-INTERNAL: Using these outside of exhaustively-tested folly
+// internals (result.h, Try.h) risks unannounced UB and build breaks.
+//
+// IMPORTANT: Values must have the low 3 bits clear (see "packed" storage).
+enum class private_rich_exception_ptr_sigil : uintptr_t {
+  // Used by `result` to indicate the value union member is active.
+  RESULT_HAS_VALUE = 0,
+  // Reserved for a potential future implementation of `Try` via `result`.
+  // IMPORTANT: Must NEVER be ingestable into a user-visible `result`.
+  EMPTY_TRY = 8,
+};
+
+class rich_exception_ptr_base_storage {
+ protected:
+  // Non-owning view of type-punned storage of the `std::exception_ptr` stored
+  // in a `uintptr_t` array.
+  struct const_eptr_ref_guard {
+    // On GNU systems `exception_ptr` contains 1 pointer; MSVC uses 2.
+    // See https://devblogs.microsoft.com/oldnewthing/20200820-00/?p=104097
+    static_assert(sizeof(std::exception_ptr) % sizeof(uintptr_t) == 0);
+    static constexpr size_t detail_num_ptrs_in_exception_ptr =
+        sizeof(std::exception_ptr) / sizeof(uintptr_t);
+#if defined(_WIN32)
+    static_assert(detail_num_ptrs_in_exception_ptr == 2);
+#else
+    static_assert(detail_num_ptrs_in_exception_ptr == 1);
+#endif
+
+    // NB: Since, in practice, all major compiler support type-punning through
+    // unions, it'd be safer / cleaner to have an anon-union of either the
+    // `uintptr_t`s (so packed storage can touch the lower bits) or a
+    // `std::exception_ptr`.  Unfortunately, the latter would prevent the use
+    // of this type in constant-evaluated code, so we have to stick with the
+    // riskier `reinterpret_cast`.
+    alignas(std::exception_ptr)
+        uintptr_t uintptrs_[detail_num_ptrs_in_exception_ptr];
+
+    const std::exception_ptr& ref() const {
+      return *std::launder(
+          reinterpret_cast<const std::exception_ptr*>(uintptrs_));
+    }
+  };
+  // Separate type so `rich_exception_ptr_packed_storage::get_eptr_ref_guard`
+  // can propagate `const`.
+  struct mutable_eptr_ref_guard : const_eptr_ref_guard {
+    std::exception_ptr& ref() {
+      return *std::launder(reinterpret_cast<std::exception_ptr*>(uintptrs_));
+    }
+  };
+
+  // On supported platforms, we overwrite the alignment bits (the low 3) of
+  // `sigil_` (aka `immortal_storage_` aka `eptr_ref_guard_.uintptrs_[0]`) to
+  // store `bits_`.  Note that on Windows, both indices 0 & 1 in
+  // `eptr_ref_guard_.uintptrs_` are pointers.
+  //
+  // We check that the 3 bits are free via `static_assert` & `debug_assert`.
+  //
+  // NB Here, we are not constrained by the alignment requirements of any
+  // "small value" type, since it is not directly accessed from this storage.
+  // Rather, it is demangled by `small_value_ref_guard` from `get_uintptr()`.
+  union
+      // Below, we assert that eptr has the highest alignment of these members.
+      alignas(std::exception_ptr) data_t {
+    // (1) Used as a convenience to access packed bits for all union states.
+    // (2) With `SIGIL_eq == bits`, stores a `private_rich_exception_ptr_sigil`.
+    // (3) With `SMALL_VALUE_eq == bits`, stores a bit-mangled small value.
+    uintptr_t uintptr_;
+    // NB: This {}-init is here to silence a linter.  It's not meant to do
+    // anything, although it looks like it MIGHT slightly shift benchmarks.
+    // Leaving it be, it's not yet worthwhile to really quantify its impact.
+    const detail::immortal_exception_storage* immortal_storage_{};
+    // Used for "owned eptr" and (non-owned) "nothrow OC leaky singleton eptr".
+    mutable_eptr_ref_guard eptr_ref_guard_;
+  };
+  static_assert(
+      alignof(std::exception_ptr) >=
+      alignof(detail::immortal_exception_storage*));
+  static_assert(alignof(std::exception_ptr) >= alignof(uintptr_t));
+
+  // The setup of `bits_t` is a bit subtle, see `rich_exception_ptr.md`.  This
+  // lets us turn the bottom 3 alignment bits of the pointer into a union-type
+  // selector, letting one pointer represent:
+  //   - `exception_ptr`
+  //   - Small values
+  //   - Sigil storage, see `private_rich_exception_ptr_sigil`
+  //   - Immortal `rich_error`s (think error codes + strings)
+  //
+  // These also support a few fast non-RTTI type queries.  Is this
+  // `exception_ptr` known either to contain, or not to contain, a fast-path
+  // type like cancellation or `rich_error`?
+  //
+  // The bit names have lowercase suffixes / prefixes that show how they're
+  // meant to be used in tests:
+  //   (FOO_and & bits_)
+  //   FOO_eq == bits_
+  //   FOO_masked_eq = (bits_ & mask_BAR)
+  //
+  // CRITICAL:
+  // CRITICAL: DO NOT use values > 7 in the enum!
+  // CRITICAL:
+  friend constexpr bool test_rich_exception_ptr_bits();
+  enum bits_t : uintptr_t {
+    // If this bit 1, the union is an `std::exception_ptr` we own and must
+    // destroy.  Its type is described by `mask_FAST_PATH_TYPES` bits.
+    OWNS_EXCEPTION_PTR_and = 1,
+
+    // These bits means empty `exception_ptr` when `immortal_storage_` is null.
+    IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq = 0,
+    // Future small-value optimization.  The idea is to inline `result<T>` for
+    // trivial `T` of <= 61 bits modulo alignment, like `int` or `void&`.
+    SMALL_VALUE_eq = 6,
+    // When `rich_exception_ptr` stores a `private_rich_exception_ptr_sigil`,
+    // the sigil value is in `...uintptr_`.
+    SIGIL_eq = 2,
+    // `...eptr_ref_guard_` contains an UNOWNED copy of a leaky singleton of
+    // `StoppedNoThrow`, which propagates through coros without
+    // throwing, or being interrupted by `co_awaitTry`.
+    NOTHROW_OPERATION_CANCELLED_eq = 4,
+
+    // This mask applies to all subsequent `..._masked_eq` bit values.
+    //
+    // The use-case is fast, RTTI-free lookup for `get_exception<T>()` for a
+    // few "fast-path" exception types.
+    mask_FAST_PATH_TYPES = 6,
+
+    // (1) and (2) two are valid regardless of `OWNS_EXCEPTION_PTR`
+    //
+    // They both require that the exception type be statically known when the
+    // `rich_exception_ptr` is constructed.
+
+    // (1) `IS_RICH_ERROR_BASE_masked_eq` means that pointed-to exception has
+    // `rich_error_base` at offset 0, asserted via `has_offset0_base`.
+    //
+    // This bit value is 0 so that when we put `bits_t` in the lower 3 bits of
+    // the pointer, `rich_exception_ptr` remains `constexpr` -- in C++20,
+    // `constexpr` can only create aligned pointers.
+    //
+    // There's an implementation CAVEAT explaining why we don't ingest, for
+    // example, `unique_ptr<Ex>`.  If a user class derived both from
+    // `rich_error_base` and `T`, then an instance pointer could theoretically
+    // be sliced to `T*` before we see it.  If that happened, fast-path
+    // `get_exception<rich_error_base>()` would give a different answer than
+    // RTTI.  Fortunately:
+    //   - By-value construction of `rich_exception_ptr` is safe, and
+    //   - `immortal_rich_error_t` also avoids the risk by using a non-type
+    //     template parameter.
+    IS_RICH_ERROR_BASE_masked_eq = 0,
+    // (2) Fast path for `get_exception<OperationCancelled>()`.  Its fast-path
+    // bits deliberately match `NOTHROW_OPERATION_CANCELLED_eq`.  See the notes
+    // in `rich_exception_ptr.md` to learn why & how we store an instance
+    // pointer along with the type bits.  There is no object slicing risk --
+    // users cannot extend this hierarchy.
+    IS_OPERATION_CANCELLED_masked_eq = 4,
+
+    // (3) and (4) are only used if `OWNS_EXCEPTION_PTR` is set.
+
+    // (3) Fast negative for the above optimized `get_exception<>()` calls.
+    // Also requires the exception type to be statically known.
+    owned_eptr_KNOWN_NON_FAST_PATH_TYPE_masked_eq = 6,
+    // (4) Slow path: needs RTTI
+    owned_eptr_UNKNOWN_TYPE_masked_eq = 2,
+  };
+
+  // `FOLLY_SAFE_DCHECK` doesn't work in `constexpr`, so we have `debug_assert`.
+#ifdef NDEBUG
+  FOLLY_ALWAYS_INLINE static void debug_assert(const char*, bool) {}
+#else
+  static void debug_assert(const char*, bool);
+#endif
+  // Since it's UB to throw an empty eptr, we terminate instead
+  [[noreturn]] static void terminate_on_empty_or_invalid_eptr(bits_t);
+
+  static bool bits_store_eptr(bits_t bits) {
+    return (OWNS_EXCEPTION_PTR_and & bits) ||
+        NOTHROW_OPERATION_CANCELLED_eq == bits;
+  }
+};
+// All storage is private to `rich_exception_ptr_{packed,separate}_storage`.
+// This enforces information hiding -- all data / bit access must use the API.
+static_assert(std::is_empty_v<rich_exception_ptr_base_storage>);
+
+// "Separate" and "packed" storage accessors must:
+//   - behave uniformly across "separate" and "packed" storage,
+//   - check the bits are in an appropriate state, when possible,
+//   - validate that potentially-overlapping data correctly clears the low bits
+//     that "packed" needs to use.
+//
+// Yes, both storage classes are constructed uninitialized. That's by design.
+// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+class rich_exception_ptr_separate_storage
+    : public rich_exception_ptr_base_storage {
+ private:
+  data_t data_;
+  bits_t bits_;
+
+ protected:
+  constexpr void apply_bits_after_setting_data_with(auto data_fn, bits_t bits) {
+    data_fn(data_);
+    if (std::is_constant_evaluated()) {
+      if (bits) {
+        // NOLINTNEXTLINE(facebook-hte-ThrowNonStdExceptionIssue)
+        throw "Expect bits to be zero in consteval code";
+      }
+    } else {
+      debug_assert("separate apply_bits data", !(data_.uintptr_ & 0x7));
+      debug_assert("separate apply_bits bits", bits <= 0x7);
+    }
+    bits_ = bits;
+  }
+
+  constexpr bits_t get_bits() const {
+    if (std::is_constant_evaluated() && bits_ != 0) {
+      // NOLINTNEXTLINE(facebook-hte-ThrowNonStdExceptionIssue)
+      throw "Expect bits to be zero in consteval code";
+    }
+    return bits_;
+  }
+
+  // CAREFUL: Lacks a bits `debug_assert` because its 2 callsites have one.
+  constexpr const detail::immortal_exception_storage*
+  get_immortal_storage_or_punned_uintptr() const {
+    return data_.immortal_storage_;
+  }
+
+  uintptr_t get_uintptr() const {
+    debug_assert(
+        "get_uintptr separate",
+        SIGIL_eq == get_bits() || SMALL_VALUE_eq == get_bits());
+    return data_.uintptr_;
+  }
+
+  const const_eptr_ref_guard& get_eptr_ref_guard() const {
+    debug_assert("separate get_eptr_ref_guard", bits_store_eptr(get_bits()));
+    return data_.eptr_ref_guard_;
+  }
+  mutable_eptr_ref_guard& get_eptr_ref_guard() {
+    debug_assert("separate get_eptr_ref_guard", bits_store_eptr(get_bits()));
+    return data_.eptr_ref_guard_;
+  }
+};
+static_assert(
+    sizeof(rich_exception_ptr_separate_storage) ==
+    sizeof(std::exception_ptr) + sizeof(uintptr_t));
+
+inline constexpr int min_alignment_for_packed_rich_exception_ptr = 8; // 3 bits
+
+// Storage contract documented on `rich_exception_ptr_separate_storage`.
+//
+// Check `is_supported` before using "packed" storage!  It will hide union bits
+// in the low 3 bits of the first `uintptr_t` of the data, so those MUST be
+// guaranteed to be empty. Here are the criteria from `is_supported`:
+//   - When the union stores `immortal_exception_storage*`, check the
+//     alignment of that stored type.
+//   - When the union stores `std::exception_ptr`, we have to rely on
+//     platform implementation details.  On all supported platforms, the
+//     first `uintptr_t` of the eptr is a pointer.  On Itanium ABI platforms,
+//     `__cxa_allocate_exception` only takes `thrown_size`, so it necessarily
+//     has to store the exception with the maximum fundamental alignment.  On
+//     Windows, the alignment situation is not documented, but it would be
+//     quite broken if it didn't at least provide alignment suitable for
+//     `std::exception`, so we test that.
+//
+// Future: If we have to support more than libgcc/libstdc++/MSVC, this might
+// merit some allow-list-by-platform conditional compilation.
+class rich_exception_ptr_packed_storage
+    : public rich_exception_ptr_base_storage {
+ private:
+  data_t data_;
+
+ public:
+  static constexpr bool is_supported = // Explained above
+      alignof(detail::immortal_exception_storage) >=
+          min_alignment_for_packed_rich_exception_ptr &&
+      alignof(std::max_align_t) >=
+          min_alignment_for_packed_rich_exception_ptr &&
+      alignof(std::exception) >= min_alignment_for_packed_rich_exception_ptr;
+
+ protected:
+  template <typename T = rich_exception_ptr_packed_storage>
+    requires T::is_supported
+  constexpr void apply_bits_after_setting_data_with(auto data_fn, bits_t bits) {
+    data_fn(data_);
+    if (std::is_constant_evaluated()) {
+      if (bits) {
+        // NOLINTNEXTLINE(facebook-hte-ThrowNonStdExceptionIssue)
+        throw "Expect bits to be zero in consteval code";
+      }
+    } else {
+      debug_assert("packed apply_bits data", !(data_.uintptr_ & 0x7));
+      debug_assert("packed apply_bits bits", bits <= 0x7);
+      data_.uintptr_ = data_.uintptr_ | bits;
+    }
+  }
+
+  constexpr bits_t get_bits() const {
+    if (std::is_constant_evaluated()) {
+      // Consteval code paths cannot get bit-level access to pointers, so we
+      // must assume 0.  `rich_exception_ptr_separate_storage::get_bits` and
+      // `rich_exception_ptr_packed_storage::apply_bits...` assert this.
+      return static_cast<bits_t>(0);
+    }
+    return static_cast<bits_t>(data_.uintptr_ & 0x7);
+  }
+
+  // CAREFUL: Lacks a bits `debug_assert` because its 2 callsites have one.
+  constexpr const detail::immortal_exception_storage*
+  get_immortal_storage_or_punned_uintptr() const {
+    return data_.immortal_storage_;
+  }
+
+  uintptr_t get_uintptr() const {
+    debug_assert(
+        "get_uintptr packed",
+        SIGIL_eq == get_bits() || SMALL_VALUE_eq == get_bits());
+    return (~(uintptr_t)0x7) & data_.uintptr_;
+  }
+
+ private:
+  mutable_eptr_ref_guard get_eptr_ref_guard_impl() const {
+    debug_assert("packed get_eptr_ref_guard", bits_store_eptr(get_bits()));
+    mutable_eptr_ref_guard ep_guard = data_.eptr_ref_guard_;
+    ep_guard.uintptrs_[0] = ~(uintptr_t)0x7 & ep_guard.uintptrs_[0];
+    return ep_guard;
+  }
+
+ protected:
+  const_eptr_ref_guard get_eptr_ref_guard() const {
+    return get_eptr_ref_guard_impl();
+  }
+  mutable_eptr_ref_guard get_eptr_ref_guard() {
+    return get_eptr_ref_guard_impl();
+  }
+};
+static_assert(
+    sizeof(rich_exception_ptr_packed_storage) == sizeof(std::exception_ptr));
+
+} // namespace folly::detail
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/epitaph.cpp b/folly/folly/result/epitaph.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/epitaph.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/result/epitaph.h>
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+
+#if FOLLY_HAS_RESULT
+
+namespace folly::detail {
+
+folly::source_location epitaph_non_value::source_location() const noexcept {
+  return msg_.location();
+}
+
+const char* epitaph_non_value::partial_message() const noexcept {
+  return msg_.message();
+}
+
+const rich_exception_ptr* epitaph_non_value::next_error_for_epitaph()
+    const noexcept {
+  return &next_;
+}
+
+} // namespace folly::detail
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/epitaph.h b/folly/folly/result/epitaph.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/epitaph.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/lang/SafeAssert.h>
+#include <folly/result/result.h>
+#include <folly/result/rich_error.h>
+#include <folly/result/rich_exception_ptr.h>
+#include <folly/result/rich_msg.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+namespace detail {
+
+// `detail::epitaph_non_value` is the error type that wraps user errors to
+// capture their provenance.
+//
+// NOTE: We don't forward any `rich_error_base` APIs to the underlying error,
+// since this would cause weird duplication in the formatted output, and ...
+// in any case, the user can only observe the wrapping error instance via the
+// "low-visibility" API of `get_outer_exception`.
+//
+// IMPORTANT: We should NEVER support adding codes via "transparent" epitaphs.
+//
+// The rationale is the same as why we work hard to expose only the underlying
+// error's dynamic type (e.g. `get_rich_error` returns `nullptr` when given a
+// epitaph wrapper around a non-rich error).
+//
+// Specifically, codes often direct program control flow, whereas epitaphs
+// are inherently discardable.  There is simply no way for us to guarantee that
+// codes added via the "epitaphs" mechanism would not be accidentally
+// discarded, be it by throwing, by using `std::exception_ptr`, or by
+// deliberately accessing the underlying error.
+class epitaph_non_value : public rich_error_base {
+ private:
+  rich_exception_ptr next_;
+  rich_msg msg_;
+
+  void set_underlying_error_on_copy_from(const epitaph_non_value& other) {
+    FOLLY_SAFE_DCHECK(other.underlying_error());
+    if (other.underlying_error() == &other.next_) {
+      set_underlying_error(underlying_error_private_t{}, &next_);
+    } else if (auto* next_rex = next_.get_outer_exception<rich_error_base>()) {
+      // If "underlying" doesn't point at `next_`, it points at something
+      // **owned** by `next_`, which means an address can only be safely taken
+      // after we copy / move it in.
+      //
+      // It is tempting to try to micro-optimize this by making "copy" act as
+      // "move" for this class, but that would break badly if someone threw an
+      // error with epitaphs -- copies during throw are quite common.  Less
+      // crucially, we would have to assume that "errors owned by `next_`" are
+      // address-stable, though today this is indeed guaranteed.
+      //
+      // NB: This has to use `mutable_...` to let `rich_exception_ptr` provide
+      // `get_mutable_exception`, but per the doc on that `rich_error_base`
+      // API, we must be careful not to mutate the underlying REP object.
+      underlying_error_private_t priv;
+      set_underlying_error(priv, next_rex->mutable_underlying_error(priv));
+    }
+  }
+
+ public:
+  ~epitaph_non_value() override = default;
+  // IMPORTANT: Custom ctors are required because `underlying_ptr_` points into
+  // `this` or data-owned-by-`this`, and thus must be relocated each time.
+  epitaph_non_value(const epitaph_non_value& other)
+      : next_{other.next_}, msg_{other.msg_} {
+    set_underlying_error_on_copy_from(other);
+  }
+  epitaph_non_value(epitaph_non_value&& other) noexcept
+      : next_{std::move(other.next_)}, msg_{std::move(other.msg_)} {
+    set_underlying_error_on_copy_from(other);
+  }
+  // These could be implemented (carefully!), but weren't yet needed.
+  epitaph_non_value& operator=(const epitaph_non_value&) = delete;
+  epitaph_non_value& operator=(epitaph_non_value&&) = delete;
+
+  // Construct only via `epitaph()`!
+  explicit epitaph_non_value(rich_exception_ptr&& next, rich_msg msg)
+      : next_{std::move(next)}, msg_{std::move(msg)} {
+    // This class should work with an empty `next_`, but it suggests user
+    // error -- normal `result` usage never creates empty exception pointers.
+    FOLLY_SAFE_DCHECK(next_ != rich_exception_ptr{});
+    // Here, we store a pointer into `this`, or something owned-by-`this`.
+    // This is only safe because of the special copy/move ctors above.
+    if (auto* rex = next_.get_outer_exception<rich_error_base>()) {
+      underlying_error_private_t priv;
+      // Same note re `mutable_...` as in `set_underlying_error_on_copy_from`.
+      if (auto* underlying = rex->mutable_underlying_error(priv)) {
+        set_underlying_error(priv, underlying);
+        return;
+      }
+    }
+    set_underlying_error(underlying_error_private_t{}, &next_);
+  }
+
+  folly::source_location source_location() const noexcept override;
+  const char* partial_message() const noexcept override;
+
+  const rich_exception_ptr* next_error_for_epitaph() const noexcept override;
+
+  using folly_get_exception_hint_types = rich_error_hints<epitaph_non_value>;
+};
+
+} // namespace detail
+
+/// epitaph
+///
+/// You can add epitaphs to errors in `result` & `error_or_stopped` with
+/// allocation-free literals, or via `fmt::format`), and source locations:
+///
+///   r = epitaph(my_result()) // only the source location
+///   r = epitaph(my_result(), "ctx") // location & string literal
+///   r = epitaph(my_result(), "fmt {}", a) // formatted, on heap
+///
+/// This takes ownership of the 1st argument, and returns a same-type value.
+///
+/// Thread-safety: The underlying exception MAY BE MUTATED, if it derives from
+/// `rich_error_base`.  Do NOT allow concurrent access to exception objects!
+///
+/// If the input is in a value state, it is not changed.  Non-value states (both
+/// error & stopped) are annotated with epitaphs (current location & message).
+///
+/// Crucially, `epitaph` never changes the type nor the
+/// `get_rich_error_code()`s of the error -- access to both will work the same
+/// as before adding epitaphs.  So, this works, as do `has_stopped()` checks:
+///
+///   eos = epitaph(error_or_stopped{std::logic_error{"BUG"}}, "AT");
+///   if (auto ex = get_exception<std::logic_error>()) { // NOT `auto*`!
+///     LOG(INFO) << ex; // Prints: AT @ src.cpp:42 -> BUG
+///   }
+///
+/// For a wrapper that can change codes, check out `nestable_coded_rich_error`.
+///
+/// How epitaphs work under the hood:
+///
+///   - The inner `logic_error` is wrapped with a `rich_error` , but this is
+///     not observable via APIs besides `get_outer_exception()`.  Seek `result`
+///     maintainer advice before using this function!
+///
+///   - For normal error access, our `get_exception` implementation returns a
+///     special `rich_ptr_to_underlying_error` that quacks like a pointer to
+///     the underlying error, but prints the epitaph stack when formatted.
+///
+/// On the "value" path, the perf cost is minimal -- 1 branch.  On the "error"
+/// path, adding epitaphs **may** allocate a new `std::exception_ptr` (now
+/// 60ns for ctor + dtor, could use some micro-optimization), but given demand
+/// we can amortize this to 5ns per call.
+///
+/// Epitaphs work regardless of the underlying exception type. Epitaph
+/// data are exposed in an RTTI-free way via `rich_error_base`, so non-rich
+/// errors are necessarily wrapped.
+///
+/// Future: `epitaphs.md` has pointers on how the epitaphs support
+/// should evolve (for better perf & usability).
+template <typename... Args>
+error_or_stopped epitaph(
+    error_or_stopped eos,
+    // The `format_string_and_location` doc explains the `type_identity`
+    ext::format_string_and_location<std::type_identity_t<Args>...> snl = "",
+    Args const&... args) {
+  return error_or_stopped{rich_error<detail::epitaph_non_value>{
+      // No exception handling since `exception_ptr` creation currently is
+      // `noexcept`, meaning that it either succeeds by using some "reserved
+      // memory" if provided by the `std` implementation, or terminates.
+      std::move(eos).release_rich_exception_ptr(),
+      rich_msg{std::move(snl), args...}}};
+}
+
+template <typename T, typename... Args>
+result<T> epitaph(
+    result<T> r,
+    // The `error_or_stopped` overload explains the `type_identity`.
+    ext::format_string_and_location<std::type_identity_t<Args>...> snl = "",
+    Args const&... args) {
+  if (r.has_value()) {
+    return r;
+  }
+  return epitaph(std::move(r).error_or_stopped(), std::move(snl), args...);
+}
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/errc_rich_error.cpp b/folly/folly/result/errc_rich_error.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/errc_rich_error.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/result/errc_rich_error.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace fmt {
+
+fmt::appender formatter<folly::detail::errc_format_as>::format(
+    folly::detail::errc_format_as ec, format_context& ctx) const {
+  return fmt::format_to(
+      ctx.out(),
+      "{} ({})",
+      static_cast<int>(ec.code),
+      std::make_error_code(ec.code).message());
+}
+
+} // namespace fmt
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/errc_rich_error.h b/folly/folly/result/errc_rich_error.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/errc_rich_error.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <system_error>
+
+#include <folly/result/coded_rich_error.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+namespace detail {
+// Wrapper to format `std::errc` as a rich error code. Avoids ODR issues from
+// specializing `fmt::formatter<std::errc>`, which `fmt` may eventually provide.
+struct errc_format_as {
+  std::errc code;
+  explicit errc_format_as(std::errc c) : code(c) {}
+};
+} // namespace detail
+
+// Specialization of `rich_error_code` for `std::errc`.
+// This allows using `std::errc` with `get_rich_error_code()`.
+template <>
+struct rich_error_code<std::errc> {
+  // Do not change this random number, it's part of the ABI (see the docs).
+  static constexpr uint64_t uuid = 14874560630508665473ULL;
+  static constexpr const char* name = "std::errc";
+  using format_as_t = detail::errc_format_as;
+};
+
+// This is the rich-error counterpart to `std::system_error`.  But, first get
+// familiar with `docs/rich_error_code.md`, and `coded_rich_error.h`.
+// Instantiate via:
+//   make_coded_rich_error(std::errc::whatever, "message {}", arg)
+//   errc_rich_error::make(...)
+//
+// Note that this is implementation is POSIX-centric, and that the first person
+// wanting to deal with Windows codes may want to add an derived type that
+// exports both `std::errc` and a Windows-centric enum.  Do get careful review
+// of the design if you intend to add it to `folly/result`.
+using errc_rich_error = coded_rich_error<std::errc>;
+
+} // namespace folly
+
+template <>
+struct fmt::formatter<folly::detail::errc_format_as> {
+  constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
+  fmt::appender format(
+      folly::detail::errc_format_as ec, format_context& ctx) const;
+};
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/gtest_helpers.h b/folly/folly/result/gtest_helpers.h
--- a/folly/folly/result/gtest_helpers.h
+++ b/folly/folly/result/gtest_helpers.h
@@ -17,20 +17,16 @@
 #pragma once
 
 #include <folly/coro/GtestHelpers.h>
-#include <folly/result/result.h>
+#include <folly/result/coro.h>
 
 namespace folly {
 
-#define RESULT_CO_UNWRAP_BODY(body)                                  \
-  {                                                                  \
-    auto ret = body();                                               \
-    if (!ret.has_value()) {                                          \
-      if (ret.non_value().has_stopped()) {                           \
-        FAIL() << "RESULT_CO_TEST got cancellation";                 \
-      } else {                                                       \
-        FAIL() << std::move(ret).non_value().to_exception_wrapper(); \
-      }                                                              \
-    }                                                                \
+#define RESULT_CO_UNWRAP_BODY(body)     \
+  {                                     \
+    auto ret = body();                  \
+    if (!ret.has_value()) {             \
+      FAIL() << ret.error_or_stopped(); \
+    }                                   \
   }
 
 /*
@@ -45,7 +41,7 @@
       test_name,                                  \
       ::testing::Test,                            \
       ::testing::internal::GetTestTypeId(),       \
-      result<>,                                   \
+      ::folly::result<>,                          \
       RESULT_CO_UNWRAP_BODY)
 
 /*
@@ -60,7 +56,7 @@
       test_name,                                      \
       test_fixture,                                   \
       ::testing::internal::GetTypeId<test_fixture>(), \
-      result<>,                                       \
+      ::folly::result<>,                              \
       RESULT_CO_UNWRAP_BODY)
 
 } // namespace folly
diff --git a/folly/folly/result/immortal_rich_error.h b/folly/folly/result/immortal_rich_error.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/immortal_rich_error.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/Traits.h>
+#include <folly/result/detail/ptr_immortal_exception_storage.h>
+#include <folly/result/detail/rich_error_common.h>
+#include <folly/result/rich_exception_ptr.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+template <typename, typename, auto...>
+class immortal_rich_error_t;
+
+namespace detail {
+
+// Only this class and `rich_error` can instantiate `rich_error_base`!
+template <typename UserBase>
+class immortal_rich_error_storage final
+    : public detail::rich_error_with_partial_message<UserBase> {
+ private:
+  template <typename, typename, auto...>
+  friend class folly::immortal_rich_error_t;
+
+  using detail::rich_error_with_partial_message<
+      UserBase>::rich_error_with_partial_message;
+
+  void only_rich_error_may_instantiate(
+      rich_error_base::only_rich_error_may_instantiate_t) override {}
+
+ public:
+  // Don't use, only exposed for `ptr_immortal_exception_storage`
+  using folly_private_immortal_rich_error_base = UserBase;
+};
+
+} // namespace detail
+
+/// immortal_rich_error<T, CtorArgs...>
+///
+/// Demo usage:
+///
+///   struct MyErr : rich_error_base {
+///     using folly_get_exception_hint_types = rich_error_hints<MyErr>;
+///   };
+///
+///   static constexpr auto complex_arg = make_complex_arg();
+///   inline constexpr rich_exception_ptr leet_my_err =
+///       immortal_rich_error<MyErr, &complex_arg, 1337>::ptr();
+///
+/// In large programs, you will want to split definitions / declarations of the
+/// `rich_exception_ptr` across `.cpp` / `.h` and mark them `constinit`.
+///
+/// If the ctor of `YourErr` takes `rich_msg`, you can pass string literals as
+/// `immortal_rich_error<YourErr, "foo"_litv>` -- see examples in tests.
+///
+/// Implementation notes:
+///   - The REP param is for tests. In prod, it is always `rich_exception_ptr`.
+///   - This template supports taking `rich_error<MyErr>` to encourage end
+///     users to alias their `rich_error`, and to hide the wrapper.  The goal?
+///     -- just one identifier, `MyErr`, to remember.
+///   - Taking the pointer as a template param ensures it's immortal.
+///   - Per https://timsong-cpp.github.io/cppwp/n4868/temp.arg.nontype,
+///     template params cannot point to sliced subobjects.  This avoids the
+///     slicing risk from the `IS_RICH_ERROR_BASE` doc.
+template <typename REP /* = rich_exception_ptr */, typename T, auto... Args>
+class immortal_rich_error_t final {
+ private:
+  // Make `immortal_rich_error<rich_error<T>>` work as `immortal_rich_error<T>`.
+  using UserBase =
+      detected_or_t<T, detail::detect_folly_detail_base_of_rich_error, T>;
+  // Since `std::exception` isn't constexpr until C++26, we can only
+  // instantiate the user-provided base at compile-time.  That's all we need
+  // for most operations on your immortal `rich_exception_ptr rep`.
+  //
+  // These two accesses lazily create an immutable, indestructible singleton
+  // with a `rich_error<UserBase>` copy of your error:
+  //   - `get_exception<std::exception>(rep)`
+  //   - `get_exception<rich_error<UserBase>>(rep)`
+  //
+  // You will get a DIFFERENT mutable, indestructible singleton if you do:
+  //   - `rep.to_exception_ptr_slow()`
+  //   - `get_mutable_exception<Ex>(rep)`
+  //
+  // To recap: `const` operations on immortals always access the `constexpr`
+  // exception, or a copy -- but, mutable operations use a separate singleton.
+  constexpr static detail::immortal_rich_error_storage<UserBase> immortal_{
+      Args...};
+  constexpr static detail::ptr_immortal_exception_storage<&immortal_>
+      storage_{};
+  // Future: In applications with MANY errors, or where binary size is at a
+  // premium, we could construct this bit at runtime at low cost.  This is also
+  // a path to supporting non-rich immortals, if needed.
+  constexpr static REP ptr_{&immortal_, &storage_};
+
+ public:
+  [[nodiscard]] constexpr static const REP& ptr() {
+    // Has a manual test, but the build doesn't get this far.
+    detail::static_assert_is_valid_rich_error_type<UserBase>(&immortal_);
+    return ptr_;
+  }
+};
+template <typename T, auto... Args>
+inline constexpr immortal_rich_error_t<rich_exception_ptr, T, Args...>
+    immortal_rich_error;
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/nestable_coded_rich_error.h b/folly/folly/result/nestable_coded_rich_error.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/nestable_coded_rich_error.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/result/coded_rich_error.h>
+#include <folly/result/rich_error.h>
+#include <folly/result/rich_error_base.h>
+#include <folly/result/rich_exception_ptr.h>
+#include <folly/result/rich_msg.h>
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+namespace detail {
+
+template <typename Derived, bool InheritCodes, typename... Codes>
+class nestable_coded_rich_error_crtp : public coded_rich_error<Codes...> {
+ private:
+  rich_exception_ptr next_{};
+
+ public:
+  // Future: Could add `make()` a la `coded_rich_error::make`, if useful.
+
+  // Avoid. For ergonomics, prefer `make_nestable_coded_rich_error()` to
+  // construct `rich_error<nestable_coded_rich_error>{...}`.
+  explicit constexpr nestable_coded_rich_error_crtp(
+      Codes... codes, rich_exception_ptr next, rich_msg msg)
+      : coded_rich_error<Codes...>{codes..., std::move(msg)},
+        next_{std::move(next)} {}
+
+  const rich_exception_ptr* next_error_for_epitaph() const noexcept override {
+    return (next_ != rich_exception_ptr{}) ? &next_ : nullptr;
+  }
+
+  constexpr void retrieve_code(rich_error_code_query& q) const override {
+    coded_rich_error<Codes...>::retrieve_code(q);
+    if constexpr (InheritCodes) {
+      if (!q.has_value()) {
+        if (auto ex = folly::get_exception<folly::rich_error_base>(next_)) {
+          ex->retrieve_code(q);
+        }
+      }
+    }
+  }
+
+  using folly_get_exception_hint_types = rich_error_hints<Derived>;
+};
+
+} // namespace detail
+
+/// nestable_coded_rich_error
+///
+/// Like `coded_rich_error` (read its docs first!), plus the ability to nest
+/// around another "next" exception for better debuggability.
+///
+/// This becomes the new "underlying exception" in `rich_error_base` jargon. The
+/// nested error's type, message, location, codes are NOT exposed -- they're
+/// only visible via fmt / ostream output for debugging.
+///
+/// If you do NOT intend to "hide" the type of the old error (`next` arg), then
+/// you may be wanting the transparent annotations from `epitaph.h`.
+///
+/// Future: This currently doesn't support nesting during constant evaluation.
+/// That would require adding a consteval ctor taking `rich_exception_ptr*`,
+/// similar to `ConstErrWithNext` in `immortal_rich_error_test.cpp`.  Do add
+/// this (plus a `constexpr` test) if the need comes up.
+template <typename... Codes>
+struct nestable_coded_rich_error
+    : public detail::nestable_coded_rich_error_crtp<
+          nestable_coded_rich_error<Codes...>,
+          false,
+          Codes...> {
+  using detail::nestable_coded_rich_error_crtp<
+      nestable_coded_rich_error<Codes...>,
+      false,
+      Codes...>::nestable_coded_rich_error_crtp;
+};
+
+/// inheriting_coded_rich_error
+///
+/// Behaves almost identically to `nestable_coded_rich_error`, but it IS able to
+/// inherits codes from the nested error. For a `get_user_error_code<Code>()`
+/// query, if `Code` is in `Codes`, it will be fulfilles by this class.
+/// Otherwise, the query will be forwarded to the nested error.
+template <typename... Codes>
+struct inheriting_coded_rich_error
+    : public detail::nestable_coded_rich_error_crtp<
+          inheriting_coded_rich_error<Codes...>,
+          true,
+          Codes...> {
+  using detail::nestable_coded_rich_error_crtp<
+      inheriting_coded_rich_error<Codes...>,
+      true,
+      Codes...>::nestable_coded_rich_error_crtp;
+};
+
+/// Make `rich_error<nestable_coded_rich_error<Codes...>>` or
+/// `rich_error<inheriting_coded_rich_error<Codes...>>`, deducing code types.
+///
+/// Common case -- single code:
+///   make_..._coded_rich_error(MyCode::VAL, next, "info={}", info)
+/// Power users -- multiple codes:
+///   make_..._coded_rich_error(rich_msg{"msg"}, next, Code1::A, Code2::B)
+inline constexpr make_coded_rich_error_fn<
+    nestable_coded_rich_error,
+    rich_exception_ptr>
+    make_nestable_coded_rich_error{};
+inline constexpr make_coded_rich_error_fn<
+    inheriting_coded_rich_error,
+    rich_exception_ptr>
+    make_inheriting_coded_rich_error{};
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/or_unwind_epitaph.h b/folly/folly/result/or_unwind_epitaph.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/or_unwind_epitaph.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/result/coro.h>
+#include <folly/result/epitaph.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+/// or_unwind_epitaph
+///
+/// Syntax sugar for `or_unwind(epitaph(...)`.
+///
+/// On the value path: epitaphs are skipped (just like `epitaph`).
+/// On the error-or-stopped path: epitaphs are added, then the error is
+/// propagated.
+template <typename T, typename... Args>
+auto or_unwind_epitaph(
+    result<T> r,
+    ext::format_string_and_location<std::type_identity_t<Args>...> snl = "",
+    Args const&... args) {
+  return or_unwind_owning(epitaph(std::move(r), snl, args...));
+}
+template <typename... Args>
+auto or_unwind_epitaph(
+    error_or_stopped eos,
+    ext::format_string_and_location<std::type_identity_t<Args>...> snl = "",
+    Args const&... args) {
+  return or_unwind_owning(epitaph(std::move(eos), snl, args...));
+}
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/result.cpp b/folly/folly/result/result.cpp
--- a/folly/folly/result/result.cpp
+++ b/folly/folly/result/result.cpp
@@ -16,32 +16,35 @@
 
 #include <folly/result/result.h>
 
+#include <ostream>
+
 #include <glog/logging.h>
 #include <folly/Indestructible.h>
 
 #if FOLLY_HAS_RESULT
 
-namespace folly::detail {
+namespace folly {
+namespace detail {
 
-const non_value_result& dfatal_get_empty_result_error() {
-  static const folly::Indestructible<non_value_result> r{empty_result_error{}};
+const error_or_stopped& dfatal_get_empty_result_error() {
+  static const folly::Indestructible<error_or_stopped> r{empty_result_error{}};
   LOG(DFATAL) << "`folly::result` had an empty underlying `folly::Expected`";
   return *r;
 }
 
-const non_value_result& dfatal_get_bad_result_access_error() {
-  static const folly::Indestructible<non_value_result> r{
+const error_or_stopped& dfatal_get_bad_result_access_error() {
+  static const folly::Indestructible<error_or_stopped> r{
       bad_result_access_error{}};
   LOG(DFATAL)
-      << "Used `non_value()` accessor for `folly::result` in value state";
+      << "Used `error_or_stopped()` accessor for `folly::result` in value state";
   return *r;
 }
 
-void fatal_if_exception_wrapper_invalid(const exception_wrapper& ew) {
-  if (!ew.has_exception_ptr()) {
+void fatal_if_eptr_empty_or_stopped(const std::exception_ptr& eptr) {
+  if (!eptr) {
     LOG(FATAL) << "`result` may not contain an empty `std::exception_ptr`";
   }
-  if (folly::get_exception<folly::OperationCancelled>(ew)) {
+  if (folly::get_exception<folly::OperationCancelled>(eptr)) {
     LOG(FATAL)
         << "Do not store `OperationCancelled` in `result`. If you got this "
         << "error while extracting an `exception_wrapper`, `exception_ptr`, "
@@ -49,6 +52,12 @@
   }
 }
 
-} // namespace folly::detail
+} // namespace detail
+
+std::ostream& operator<<(std::ostream& os, const error_or_stopped& eos) {
+  return detail::ostream_write_via_fmt(os, eos);
+}
+
+} // namespace folly
 
 #endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/result.h b/folly/folly/result/result.h
--- a/folly/folly/result/result.h
+++ b/folly/folly/result/result.h
@@ -19,9 +19,11 @@
 #include <folly/ExceptionWrapper.h>
 #include <folly/Expected.h>
 #include <folly/OperationCancelled.h>
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
 #include <folly/lang/Align.h> // for `hardware_constructive_interference_size`
 #include <folly/lang/RValueReferenceWrapper.h>
 #include <folly/portability/GTestProd.h>
+#include <folly/result/rich_exception_ptr.h>
 
 /// Read the full docs in `result.md`!
 ///
@@ -33,59 +35,72 @@
 ///     as far as control flow is concerned.
 ///
 ///   - Easy exception checks:
-///       if (auto* ex = folly::get_exception<Ex>(res)) { /*...*/ }
+///       if (auto ex = folly::get_exception<Ex>(res)) {
+///         // Handle `ex`, quacks like `const Ex*`
+///       }
 ///
 ///   - User-friendly constructors & conversions -- you can write
 ///     `result<T>`-returning functions as-if they returned `T`, while returning
-///     returning `non_value_result{YourException{...}}` on error.
+///     returning `error_or_stopped{YourException{...}}` on error.
 ///
 ///   - Can store & and && references.  Think of them as syntax sugar for
 ///     `std::reference_wrapper` and `folly::rvalue_reference_wrapper`.
 ///
+///        #include <folly/result/coro.h>
 ///        struct FancyIntMap {
 ///          int n;
 ///          result<int&> at(int i) {
 ///            if (n + i == 42) { return std::ref(n); }
-///            return non_value_result{std::out_of_range{"FancyIntMap"}};
+///            return error_or_stopped{std::out_of_range{"FancyIntMap"}};
 ///          }
 ///        };
 ///        FancyIntMap m{.n = 12};
-///        int& n1 = co_await m.at(30); // points at 12
+///        int& n1 = co_await or_unwind(m.at(30)); // points at 12
 ///        result<int&> rn2 = m.at(20); // has error
-///        co_return n1 + co_await std::move(rn2); // propagates error
+///        // On error, copies and propagates `exception_ptr` to the parent.
+///        // When errors are hot, `std::move()` is appropriate.
+///        co_return n1 + co_await or_unwind(rn2);
 ///
 ///     Key things to remember:
 ///       - `result<V&&>` is "use-once" -- and it must be r-value qualified to
 ///         access the reference inside.
-///       - `const result<V&>` gives non-`const` access to `V&`, just as `const
-///         result<V*>` would.
+///       - `const result<V&>` only gives `const V&` access to the contents.
+///         It should be rare that you need the opposite behavior.  If you do,
+///         use `result<std::reference_wrapper<V>>` or `result<V*>`.
 ///
 ///   - `has_stopped()` & `stopped_result` to nudge `folly` to the C++26 idea
 ///     that cancellation is NOT an error, see https://wg21.link/P1677 & P2300.
 ///
 ///   - Easy short-circuiting of "error" / "stopped" status to the caller:
+///     * In both `result<T>` coroutines and `folly::coro` coroutines:
+///       - `co_await or_unwind(resultFn())` returns `T&&` from a `result<T>`,
+///         or propagates error/stopped to the parent. Same for:
+///           * `co_await or_unwind(std::move(res))`
+///           * `co_await or_unwind(res.copy())`
+///           * `co_await or_unwind(folly::copy(res))`
+///       - `co_await or_unwind(res) -> T&`. Copies `exception_ptr` on error.
+///       - `co_await or_unwind(std::as_const(res)) -> const T&`
+///       WARNING: `auto&& ref = co_await or_unwind(rvalueFn())` dangles; search
+///       `result.md` for "LLVM issue #177023".  Safe: `auto val = ...`
+///       - `co_await stopped_result` or `error_or_stopped{YourErr{}}` to
+///         end the coroutine with an error without throwing.
 ///     * In `folly::coro` coroutines:
-///       - `co_await co_await_result(x())` makes `result<X>`, does not throw.
-///       - `co_await co_ready(syncResultFn())` extracts `T` from a
-///          `result<T>`, or propagates error/stopped.
-///       - `co_yield co_result(std::move(res))` returns a `result<T>`.
-///     * In synchronous `result<T>` coroutines,
-///       - `co_await std::move(res)` and `folly::copy(res)` give you `T`,
-///       - `co_await std::ref(res)` gives you `T&` -- ditto for `std::cref`
-///         and `folly::cref`.
+///       - `co_await value_or_error_or_stopped(x())` makes `result<X>`, does
+///         not throw.
+///       - `co_yield co_result(std::move(res))` completes with a `result<T>`.
 ///     * While you should strongly prefer to write `result<T>` coroutines,
 ///       propagation in non-coroutine `result<T>` functions is also easy:
 ///         if (!res.has_value()) {
-///           return std::move(res).non_value();
+///           return std::move(res).error_or_stopped();
 ///         }
 ///
 ///   - `result` is mainly used for return values -- implying single ownership.
 ///     For this reason, it encourages moves over copies (with a few carve-outs
 ///     for better usability), which also helps prevent perf bugs.
 ///
-/// Note: Unlike `Try`, `non_value_result` (and thus `result<T>` in a non-value
-/// state) will `std::terminate` in debug builds if you attempt to construct it,
-/// or access it while it contains either of:
+/// Note: Unlike `Try`, `error_or_stopped` (and thus `result<T>` in an
+/// error-or-stopped state) will `std::terminate` in debug builds if you attempt
+/// to construct it, or access it while it contains either of:
 ///
 ///   - `OperationCancelled` -- the header explains why user code should not
 ///     use that exception.  Instead, store `stopped_result`, and use
@@ -104,136 +119,193 @@
 struct OperationCancelled;
 
 namespace detail {
+
 // In order to give `result` a stronger contract, debug builds prevent `result`
-// and `non_value_result` from ingesting empty `std::exception_ptr`s, and ones
+// and `error_or_stopped` from ingesting empty `std::exception_ptr`s, and ones
 // with `OperationCancelled`.
 //
 // In prod, neither check is done since the legacy behaviors are "okay"-ish:
 //   - Empty `std::exception_ptr`s, while nonsensical in the context of
 //     `result`, are safe to use unless you call `throw_exception()`.  And,
 //     unfortunately, `co_yield co_error(exception_wrapper{})` compiles.
-//   - As of 2025, erroring with `OperationCancelled` is the implementation of
-//     `co_yield co_canceled`, and some code paths actually rely on this, often
-//     erroneously (see `coro/Retry.h`).  So, even as we work to reduce
-//     reliance on this in anticipation of C++26 "stopped" semantics, for
-//     the foreseeable future it will "sort of work".
-void fatal_if_exception_wrapper_invalid(const exception_wrapper&);
-inline void dfatal_if_exception_wrapper_invalid(const exception_wrapper& ew) {
-  // This code path could be hot in production code, so there's no branch or
-  // logging in opt builds.
+//   - As of 2026, erroring with `OperationCancelled` is the implementation of
+//     `co_yield co_stopped_may_throw`, and some code paths actually rely on
+//     this, often erroneously (see `coro/Retry.h`).  So, even as we work to
+//     reduce reliance on this in anticipation of C++26 "stopped" semantics,
+//     for the foreseeable future it will "sort of work".
+void fatal_if_eptr_empty_or_stopped(const std::exception_ptr&);
+inline void dfatal_if_eptr_empty_or_stopped(const std::exception_ptr& eptr) {
+  // This can be hot in production code (usage similar to `co_awaitTry`).  So,
+  // we choose to omit `if (RTTI-test-for-stopped) { log(); }` from opt builds.
   if constexpr (kIsDebug) {
-    fatal_if_exception_wrapper_invalid(ew);
+    fatal_if_eptr_empty_or_stopped(eptr);
   }
 }
+
+// Do NOT use outside of `folly`, the APIs this gates WILL change.
+struct result_private_t {};
 } // namespace detail
 
-// Place this into `result` or `non_value_result` to signal that a work-tree
+// Place this into `result` or `error_or_stopped` to signal that a work-tree
 // was stopped (aka cancelled).  You can also `co_await stopped_result` from
 // `result` coroutines.
 struct stopped_result_t {};
 inline constexpr stopped_result_t stopped_result;
 
-// NB: Copying `non_value_result` is ~25ns due to `std::exception_ptr` atomics.
+template <typename, typename, auto...>
+class immortal_rich_error_t;
+
+// NB: Copying `error_or_stopped` is ~7ns due to `std::exception_ptr` atomics.
 // Unlike `result`, it is implicitly copyable, because:
 //   - Common usage involves only rvalues, so the risk of perf bugs is low.
 //   - `folly::Expected` assumes that the error type is copyable, and it's
 //     too convenient an implementation not to use.
-class non_value_result {
+class [[nodiscard]] error_or_stopped {
  private:
-  exception_wrapper ew_;
+  rich_exception_ptr rep_;
 
-  non_value_result(std::in_place_t, exception_wrapper ew)
-      : ew_(std::move(ew)) {}
+  error_or_stopped(std::in_place_t, std::exception_ptr&& eptr) noexcept
+      : rep_{rich_exception_ptr::from_exception_ptr_slow(std::move(eptr))} {}
 
-  template <typename Ex, typename EW>
-  static Ex* get_exception_impl(EW& ew) {
-    return folly::get_exception<Ex>(ew);
+  template <typename Ex, typename REP>
+  static Ex* get_exception_impl(REP& rep) {
+    return folly::get_exception<Ex>(rep);
   }
 
  public:
   /// Future: Fine to make implicit if a good use-case arises.
-  explicit non_value_result(stopped_result_t)
-      : ew_(make_exception_wrapper<OperationCancelled>()) {}
-  non_value_result& operator=(stopped_result_t) {
-    ew_ = make_exception_wrapper<OperationCancelled>();
+  explicit error_or_stopped(stopped_result_t) : rep_{OperationCancelled{}} {}
+  error_or_stopped& operator=(stopped_result_t) {
+    rep_ = rich_exception_ptr{OperationCancelled{}};
     return *this;
   }
 
   /// Use this ctor to report errors from `result` coroutines & functions:
-  ///   co_await non_value_result{YourError{...}};
+  ///   co_await error_or_stopped{YourError{...}};
   ///
-  /// Design note: We do NOT want most users to construct `non_value_result`
+  /// Hot error paths should consider the `immortal_rich_error_t` ctor instead.
+  ///
+  /// Design note: We do NOT want most users to construct `error_or_stopped`
   /// from type-erased `std::exception_ptr` or `folly::exception_wrapper`,
   /// because that would block RTTI-avoidance optimizations for `result` code.
-  explicit non_value_result(std::derived_from<std::exception> auto ex)
-      : ew_(std::in_place, std::move(ex)) {
+  explicit error_or_stopped(std::derived_from<std::exception> auto ex)
+      : rep_(std::move(ex)) {
     static_assert(
         !std::is_same_v<decltype(ex), OperationCancelled>,
         // The reasons for this are discussed in `folly/OperationCancelled.h`.
         "Do not use `OperationCancelled` in new user code. Instead, construct "
-        "your `result` or `non_value_result` via `stopped_result`");
+        "your `result` or `error_or_stopped` via `stopped_result`");
   }
 
-  bool has_stopped() const { return ew_.get_exception<OperationCancelled>(); }
+  /// Immortal rich errors are MUCH cheaper to instantiate than dynamic
+  /// exceptions.  This does NOT allocate a `std::exception_ptr` or perform
+  /// atomic refcount ops.
+  ///
+  /// Usage for a `YourErr` taking a single `rich_msg` constructor argument:
+  ///    error_or_stopped{immortal_error<YourErr, "msg"_litv>}
+  ///
+  /// PS These are also usable in `constexpr` code, although the current header
+  /// will need some more `contexpr` annotation to take advantage of this.
+  template <typename T, auto... Args>
+  explicit error_or_stopped(
+      const immortal_rich_error_t<rich_exception_ptr, T, Args...>& err)
+      : rep_{err.ptr()} {}
 
+  // PRIVATE, use `stopped_nothrow` (future) instead.
+  explicit error_or_stopped(detail::StoppedNoThrow s) : rep_(s) {}
+
+  [[nodiscard]] bool has_stopped() const {
+    return bool{::folly::get_exception<OperationCancelled>(rep_)};
+  }
+
   // Implement the `folly::get_exception<Ex>(res)` protocol
   template <typename Ex>
-  const Ex* get_exception(get_exception_tag_t) const noexcept {
+  rich_ptr_to_underlying_error<const Ex> get_exception(
+      get_exception_tag_t) const noexcept {
     static_assert( // Note: `OperationCancelled` is final
         !std::is_same_v<const OperationCancelled, const Ex>,
         "Test results for cancellation via `has_stopped()`");
-    return folly::get_exception<Ex>(ew_);
+    return folly::get_exception<Ex>(rep_);
   }
   template <typename Ex>
-  Ex* get_mutable_exception(get_exception_tag_t) noexcept {
+  rich_ptr_to_underlying_error<Ex> get_mutable_exception(
+      get_exception_tag_t) noexcept {
     static_assert( // Note: `OperationCancelled` is final
         !std::is_same_v<const OperationCancelled, const Ex>,
         "Test results for cancellation via `has_stopped()`");
-    return folly::get_mutable_exception<Ex>(ew_);
+    return folly::get_mutable_exception<Ex>(rep_);
   }
 
-  // AVOID. Throw-catch costs upwards of 1usec.
-  [[noreturn]] exception_wrapper throw_exception() const {
-    detail::dfatal_if_exception_wrapper_invalid(ew_);
-    ew_.throw_exception();
-  }
+  // AVOID -- throwing costs upwards of 1usec.
+  //
+  // NB: Throwing empty eptr currently terminates even in non-debug builds,
+  // following `exception_wrapper`.  Our `dfatal_if_eptr_empty_or_stopped`
+  // checks are intended to make such problems less likely, but ...  if
+  // production reliability issues trace back to this decision, it could be
+  // made to throw a private sigil instead.
+  //
+  // While user code should not intentionally rethrow `OperationCancelled` on
+  // this path, we do NOT dfatal on rethrowing `OperationCancelled` here, in
+  // contrast to `to_exception_ptr_slow()`. The motivation is:
+  //
+  //   - `result` is intended to become the next internal representation
+  //     for `folly::coro` task results.
+  //
+  //   - `value_or_throw()` is the only reasonable bridge from coro code into
+  //     non-coro code (see `blocking_wait`, `SemiFuture` conversions, etc).
+  //     On exiting coro / result code, there is no real alternative to
+  //     propagating cancellation as an exception.
+  //
+  //   - Internally, `value_or_throw()` uses `throw_exception()`.  And it would
+  //     be incoherent for one, but not the other, to be OK rethrowing
+  //     `OperationCancelled`.
+  [[noreturn]] void throw_exception() const { rep_.throw_exception(); }
 
-  /// AVOID.  Use `non_value_result(YourException{...})` if at all possible.
+  /// AVOID.  Use `error_or_stopped(YourException{...})` if at all possible.
   /// Add a `std::in_place_type_t<Ex>` constructor if needed.
   ///
-  /// Provided for compatibility with existing `exception_wrapper` code.  It
-  /// has several downsides for `result`-first code:
-  ///   - It is a debug-fatal invariant violation to pass in an
-  ///     `exception_wrapper` that is empty or has `OperationCancelled`.
-  ///     See the `dfatal_if_exception_wrapper_invalid` doc.
+  /// Provided for compatibility with existing code.  It has several downsides
+  /// for `result`-first code:
+  ///   - It is a debug-fatal invariant violation to pass in an `exception_ptr`
+  ///     that is empty or has `OperationCancelled`.
+  ///     See the `dfatal_if_eptr_empty_or_stopped` doc.
   ///   - Not knowing the static exception type blocks optimizations that can
   ///     otherwise help avoid RTTI on error paths.
-  static non_value_result from_exception_wrapper(exception_wrapper ew) {
-    detail::dfatal_if_exception_wrapper_invalid(ew);
-    return non_value_result{std::in_place, std::move(ew)};
+  static error_or_stopped from_exception_ptr_slow(std::exception_ptr eptr) {
+    detail::dfatal_if_eptr_empty_or_stopped(eptr);
+    return error_or_stopped{std::in_place, std::move(eptr)};
   }
 
   /// AVOID. Use `folly::get_exception<Ex>(r)` to check for specific exceptions.
-  /// It may be OK to add more specific accessors to `non_value_result`, see
+  /// It may be OK to add more specific accessors to `error_or_stopped`, see
   /// `throw_exception()` for an example.
   ///
   /// INVARIANT: Ensure `!has_stopped()`, or you will see a debug-fatal.
   ///
-  /// See `from_exception_wrapper` for the downsides and the rationale.
-  exception_wrapper to_exception_wrapper() && {
-    detail::dfatal_if_exception_wrapper_invalid(ew_);
-    return std::move(ew_);
+  /// See `from_exception_ptr_slow` for the downsides and the rationale.
+  [[nodiscard]] std::exception_ptr to_exception_ptr_slow() && {
+    auto eptr = std::move(rep_).to_exception_ptr_slow();
+    detail::dfatal_if_eptr_empty_or_stopped(eptr);
+    return detail::extract_exception_ptr(std::move(eptr));
   }
 
+  /// AVOID.  Most code should use `result` coros, which catch most exceptions
+  /// automatically.  Or, for a stronger guarantee, see `result_catch_all`.
+  static error_or_stopped from_current_exception() {
+    // Something was already thrown, and the user likely wants a result, so
+    // it's appropriate to accept even `OperationCancelled` here.
+    return {std::in_place, current_exception()};
+  }
+
   friend inline bool operator==(
-      const non_value_result& lhs, const non_value_result& rhs) {
-    return lhs.ew_ == rhs.ew_;
+      const error_or_stopped& lhs, const error_or_stopped& rhs) {
+    return lhs.rep_ == rhs.rep_;
   }
 
   // DO NOT USE these "legacy" functions outside of `folly` internals. Instead:
-  //   - `non_value_result(YourException{...})` whenever you statically know
+  //   - `error_or_stopped(YourException{...})` whenever you statically know
   //     the exception type (feel free to add `std::in_place_type_t` support).
-  //   - `non_value_result::from_exception_wrapper()` only when you MUST pay
+  //   - `error_or_stopped::from_exception_ptr_slow()` only when you MUST pay
   //     for RTTI, such as "thrown exceptions".
   //
   // See `OperationCancelled.h` for how to handle cancellation.  In short: use
@@ -241,17 +313,44 @@
   //
   // These internal-only functions let the `folly::coro` implementation ingest
   // `std::exception_ptr`s containing `OperationCancelled` made via
-  // `folly::coro::co_cancelled`, without incurring the 20-80ns+ cost of
-  // eagerly eagerly testing whether it contains `OperationCancelled`.
-  static non_value_result make_legacy_error_or_cancellation(
-      exception_wrapper ew) {
-    return {std::in_place, std::move(ew)};
+  // `folly::coro::co_stopped_may_throw`, without incurring the 20-80ns+ cost
+  // of eagerly testing whether it contains `OperationCancelled`.
+  static error_or_stopped make_legacy_error_or_cancellation_slow(
+      detail::result_private_t, exception_wrapper ew) {
+    return {std::in_place, std::move(ew).exception_ptr()};
   }
-  exception_wrapper get_legacy_error_or_cancellation() && {
-    return std::move(ew_);
+  exception_wrapper get_legacy_error_or_cancellation_slow(
+      detail::result_private_t) && {
+    return exception_wrapper{std::move(rep_).to_exception_ptr_slow()};
   }
+
+  // IMPORTANT: We do NOT want to provide general by-reference access to the
+  // `rich_exception_ptr` because that would e.g. put in jeopardy our ability
+  // to do `future_epitaph_in_place.md`.
+  //
+  // In particular, it is an invariant violation to call `release_...` and
+  // use the resulting reference for anything other than:
+  //   - moving out the value (if you need a copy, add an explicit
+  //     `copy_rich_exception_ptr`)
+  //   - doing nothing (i.e. deciding NOT to move the value)
+  // You are not to call `const` or mutable accessors on the resulting REP.  If
+  // you need some such form of access, you should likely extend this API.
+  rich_exception_ptr&& release_rich_exception_ptr() && {
+    return std::move(rep_);
+  }
+
+  // Implementation of `fmt::format` and `operator<<(ostream&)`.
+  void format_to(fmt::appender out) const { rep_.format_to(out); }
 };
+static_assert(
+    detail::rich_exception_ptr_packed_storage::is_supported
+        ? sizeof(error_or_stopped) == sizeof(std::exception_ptr)
+        : sizeof(error_or_stopped) ==
+            sizeof(std::exception_ptr) + sizeof(void*));
 
+// Backwards-compatible alias
+using non_value_result = error_or_stopped;
+
 template <typename T = void>
 class result;
 
@@ -260,55 +359,53 @@
 template <typename>
 struct result_promise_return;
 template <typename, typename = void>
-struct result_promise;
-struct result_await_suspender;
-
-// These errors are `detail` because they are only exposed on invariant
-// violations in opt builds -- they are NOT part of the public API.
-struct bad_result_access_error : public std::exception {};
-// Future: Remove this one when we can use never-empty `std::expected`.
-struct empty_result_error : public std::exception {};
+struct result_promise; // Build error? #include <folly/result/coro.h>
 
 // Future: To mitigate the risk of `bad_alloc` at runtime, these singletons
 // should be eagerly instantiated at program start.  One way is to have a
 // `shouldEagerInit` singleton in charge of this, and tell the users to do
 // this on startup:
 //   folly::SingletonVault::singleton()->doEagerInit();
-const non_value_result& dfatal_get_empty_result_error();
-const non_value_result& dfatal_get_bad_result_access_error();
+const error_or_stopped& dfatal_get_empty_result_error();
+const error_or_stopped& dfatal_get_bad_result_access_error();
 
 template <typename T>
-using result_ref_wrap = std::conditional_t< // Reused by `result_generator`
+using result_ref_wrap = conditional_t< // Reused by `result_generator`
     std::is_rvalue_reference_v<T>,
     rvalue_reference_wrapper<std::remove_reference_t<T>>,
-    std::conditional_t<
+    conditional_t<
         std::is_lvalue_reference_v<T>,
         std::reference_wrapper<std::remove_reference_t<T>>,
         T>>;
 
+template <typename, typename>
+class or_unwind_crtp;
+
 // Shared implementation for `T` non-`void` and `void`
 template <typename Derived, typename T>
 class result_crtp {
-  static_assert(!std::is_same_v<non_value_result, std::remove_cvref_t<T>>);
+  static_assert(
+      !std::is_same_v<class error_or_stopped, std::remove_cvref_t<T>>);
   static_assert(!std::is_same_v<stopped_result_t, std::remove_cvref_t<T>>);
 
  public:
-  using value_type = T;
+  using value_type = T; // NB: can be a reference
 
  protected:
   using storage_type = detail::result_ref_wrap<lift_unit_t<T>>;
   static_assert(!std::is_reference_v<storage_type>);
 
-  using expected_t = Expected<storage_type, non_value_result>;
+  using expected_t = Expected<storage_type, class error_or_stopped>;
 
   expected_t exp_;
 
   template <typename>
   friend class folly::result; // The simple conversion ctor uses `exp_`
 
-  friend struct detail::result_promise<T>;
-  friend struct detail::result_promise_return<T>;
-  friend struct detail::result_await_suspender;
+  friend struct result_promise<T>;
+  friend struct result_promise_return<T>;
+  template <typename, typename>
+  friend class result_or_unwind_crtp; // `await_suspend` uses `exp_`
 
   friend inline bool operator==(const result_crtp& a, const result_crtp& b) {
     // FIXME: This logic is meant to follow `std::expected`, so once that's in
@@ -328,8 +425,8 @@
       // Implicitly convert `ResultT::value_type` to `Derived`.
       return {std::forward<ResultT>(rt).value_or_throw()};
     }
-    // `Derived` lets the rewrapping conversion copy a non-value state
-    return Derived{std::forward<ResultT>(rt).non_value()}; // Rewrap non-value
+    // `Derived` lets the rewrapping conversion copy an error-or-stopped state
+    return Derived{std::forward<ResultT>(rt).error_or_stopped()};
   }
 
   struct private_copy_t {};
@@ -377,10 +474,38 @@
   /// plumbing for function-result-or-error, and
   ///   - Copying `T` is almost always a performance bug in this setting, but
   ///     see the below carve-out for "cheap-to-copy `T`".
-  ///   - Copying `std::exception_ptr` also has atomic costs (~25ns).
-  Derived copy() const {
+  ///   - Copying `std::exception_ptr` also has atomic costs (~7s).
+  ///
+  /// Future: We may later make `result` copyable, see `docs/design_notes.md`.
+  ///
+  /// ## Copies are restricted when `T` is a reference
+  ///
+  /// `const result<V&>` only gives access to `const V&`, for reasons discussed
+  /// in `result.md` under "Store references...".  Standard copy semantics
+  /// allows creating a new `result<V&>` from `const result<V&>&`.  But, this
+  /// would present a const-safety problem -- since `result<V&>` gives access
+  /// to a mutable `V&`. To fix this, copying `result<V&> is ONLY allowed:
+  ///   - from `result<V&>&`, since you already have mutable access
+  ///   - from `const result<const V&>&`, since the inner `const` is not
+  ///     lost during the copy.
+  Derived copy()
+    requires(
+        !std::is_rvalue_reference_v<T> &&
+        (std::is_void_v<T> || std::is_copy_constructible_v<T>))
+  {
     return Derived{private_copy_t{}, static_cast<const Derived&>(*this)};
   }
+  Derived copy() const
+    requires(
+        (!std::is_reference_v<T> ||
+         std::is_const_v<std::remove_reference_t<T>>) &&
+        (std::is_void_v<T> || std::is_copy_constructible_v<T>))
+  {
+    return Derived{private_copy_t{}, static_cast<const Derived&>(*this)};
+  }
+  // IMPORTANT: If you need to relax this, emulate the "restricted copy"
+  // behavior of `DefineMovableDeepConstLrefCopyable.h` when `T` is a ref.
+  // Note that this HAS to go on the leaf classes, not in the CRTP.
   result_crtp(const result_crtp&) = delete;
   result_crtp& operator=(const result_crtp&) = delete;
 
@@ -389,15 +514,17 @@
   ///
   /// This forbids `result<stopped_result_t>` (`static_assert` above).
   /*implicit*/ result_crtp(stopped_result_t s)
-      : exp_(Unexpected{non_value_result{s}}) {}
+      : exp_(Unexpected{folly::error_or_stopped{s}}) {}
 
-  /// Implicitly movable / explicitly copyable from `non_value_result` to
-  /// make it easy to return `resT1.non_value()` in a `result<T2>` function.
+  /// Implicitly movable / explicitly copyable from `error_or_stopped` to
+  /// make it easy to return `resT1.error_or_stopped()` in a `result<T2>`
+  /// function.
   ///
-  /// This forbids `result<non_value_result>` (`static_assert` above).
-  /*implicit*/ result_crtp(non_value_result&& nvr)
-      : exp_(Unexpected{std::move(nvr)}) {}
-  explicit result_crtp(const non_value_result& nvr) : exp_(Unexpected{nvr}) {}
+  /// This forbids `result<error_or_stopped>` (`static_assert` above).
+  /*implicit*/ result_crtp(class error_or_stopped&& eos)
+      : exp_(Unexpected{std::move(eos)}) {}
+  explicit result_crtp(const class error_or_stopped& eos)
+      : exp_(Unexpected{eos}) {}
 
   /// Fallible copy/move conversion -- unlike the "simple" conversion, this can
   /// plausibly apply for `T` void.
@@ -424,35 +551,35 @@
 
   /***************** Accessors for `T` `void` and non-`void` ******************/
 
-  bool has_value() const { return exp_.hasValue(); }
+  [[nodiscard]] bool has_value() const { return exp_.hasValue(); }
   // Also see `has_stopped()` below!
 
-  /// Non-value access should be used SPARINGLY!
+  /// Error-or-stopped access should be used SPARINGLY!
   ///
   /// Normally, you would:
   ///   - `folly::get_exception<Ex>(res)` to test for a specific error.
   ///   - `res.has_stopped()` to test for cancellation.
-  ///   - `co_await std::move(res)` to propagate unhandled error/cancellation
-  ///     in a `result` sync coroutine.
-  ///   - `co_await co_ready(std::move(res))` to propagate unhandled states in
-  ///     a `folly::coro` async coroutine.
+  ///   - `co_await or_unwind(std::move(res))` to propagate unhandled
+  ///     error/cancellation in `result` sync coroutines, or in `folly::coro`
+  ///     async task coroutines.
   ///
   /// Design notes:
   ///
   /// There is no mutable `&` overload so that we can return singleton
   /// invariant-violation exceptions for `dfatal_..._error()` in opt builds:
   ///   - `folly::Expected` is empty due to an `operator=` exception
-  ///   - Calling `non_value()` when `has_value() == true` -- UB in
+  ///   - Calling `error_or_stopped()` when `has_value() == true` -- UB in
   ///     `std::expected`
   /// With folly-internal optimizations (see `extract_exception_ptr`), moving
-  /// `std::exception_ptr` takes 0.5ns, vs ~25ns for a copy.
+  /// `std::exception_ptr` takes 0.5ns, vs ~7ns for a copy.
   ///
-  /// If there is a good use-case for mutating the non-value state inside
-  /// `result`, we could offer `set_non_value()` with different semantics.
+  /// If there is a good use-case for mutating the error-or-stopped state inside
+  /// `result`, we could offer `set_error_or_stopped()` with different
+  /// semantics.
   ///
   /// Future: when I have the appropriate error-path benchmark, try moving the
   /// 2 unlikely branches into a .cpp helper, that might help perf.
-  non_value_result non_value() && {
+  class error_or_stopped error_or_stopped() && noexcept {
     if (FOLLY_LIKELY(exp_.hasError())) {
       return std::move(exp_).error();
     } else if (exp_.hasValue()) {
@@ -461,7 +588,9 @@
       return detail::dfatal_get_empty_result_error();
     }
   }
-  const non_value_result& non_value() const& {
+  // noexcept: std::exception_ptr copy is non-throwing on all major platforms
+  // (atomic refcount increment via Itanium ABI or MSVC shared_ptr-like impl).
+  const class error_or_stopped& error_or_stopped() const& noexcept {
     if (FOLLY_LIKELY(exp_.hasError())) {
       return exp_.error();
     } else if (exp_.hasValue()) {
@@ -471,9 +600,19 @@
     }
   }
 
-  // Syntax sugar to minimize the chances that end-users need `non_value()`.
-  bool has_stopped() const { return !has_value() && non_value().has_stopped(); }
+  // Backwards-compatible aliases
+  class error_or_stopped non_value() && noexcept {
+    return std::move(*this).error_or_stopped();
+  }
+  const class error_or_stopped& non_value() const& noexcept {
+    return error_or_stopped();
+  }
 
+  // Syntax sugar to help end-users avoid `error_or_stopped()`.
+  [[nodiscard]] bool has_stopped() const {
+    return !has_value() && error_or_stopped().has_stopped();
+  }
+
   /********************************* Protocols ********************************/
 
   // `result` is a short-circuiting coroutine.
@@ -481,18 +620,20 @@
 
   // Implement the `folly::get_exception<Ex>(res)` protocol
   template <typename Ex>
-  Ex* get_mutable_exception(get_exception_tag_t) noexcept {
+  rich_ptr_to_underlying_error<const Ex> get_exception(
+      get_exception_tag_t) const noexcept {
     if (!exp_.hasError()) {
-      return nullptr;
+      return rich_ptr_to_underlying_error<const Ex>{nullptr};
     }
-    return folly::get_mutable_exception<Ex>(exp_.error());
+    return folly::get_exception<Ex>(exp_.error());
   }
   template <typename Ex>
-  const Ex* get_exception(get_exception_tag_t) const noexcept {
+  rich_ptr_to_underlying_error<Ex> get_mutable_exception(
+      get_exception_tag_t) noexcept {
     if (!exp_.hasError()) {
-      return nullptr;
+      return rich_ptr_to_underlying_error<Ex>{nullptr};
     }
-    return folly::get_exception<Ex>(exp_.error());
+    return folly::get_mutable_exception<Ex>(exp_.error());
   }
 };
 
@@ -500,8 +641,8 @@
 
 // The default specialization is non-`void` (but `result<>` defaults to `void`)
 template <typename T>
-class FOLLY_NODISCARD [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] result final
-    : public detail::result_crtp<result<T>, T> {
+class [[nodiscard]] [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]
+result final : public detail::result_crtp<result<T>, T> {
  private:
   template <typename, typename>
   friend class detail::result_crtp; // `ResultT::expected_t` in `requires`
@@ -567,14 +708,14 @@
   /// These are a special case because such copies are cheap*, and because
   /// good alternatives for populating trivially copiable data are few:
   ///   - Copy-construct the value into the `result`.
-  ///   - Less efficient: Default-initialize the `result` and assign a
-  ///     `folly::copy()`, or use a mutable value reference to populate it.
+  ///   - Less efficient: Default-initialize the `result` and assign
+  ///     `folly::copy(T)`, or use a mutable value reference to populate it.
   ///   - Future: Implement in-place construction, to handle very hot code.
   ///
   /// This constructor is deliberately restricted to objects that fit in a
   /// cache-line.  This is a heuristic to require larger copies to be explicit
-  /// via `folly::copy()`.  If it proves fragile across different
-  /// architectures, it can be relaxed later.
+  /// via folly::copy()`.  If it proves fragile across different architectures,
+  /// it can be relaxed later.
   ///
   /// Notes:
   ///   - For now, we omitted the analogous ctor to copy `result<V&>`, for the
@@ -592,7 +733,7 @@
       : base{std::in_place, t} {}
 
   /// No copy assignment.  When appropriate, use a mutable `value_or_throw()`
-  /// reference, or assign `folly::copy(rhs)` to be explicit.
+  /// reference, or assign `rhs.copy()` to be explicit.
 
   /// Simple copy/move conversion; `result_crtp` also has a fallible conversion.
   ///
@@ -602,11 +743,11 @@
   /// The test `simpleConversion` shows why this was made implicit.
   ///
   /// In hot code, prefer to convert from an rvalue (move conversion), because
-  /// that avoids the ~25ns atomic overhead of copying the `std::exception_ptr`.
+  /// that avoids the ~7ns atomic overhead of copying the `std::exception_ptr`.
   template <class Arg, typename ResultT = std::remove_cvref_t<Arg>>
     requires(
         !std::is_same_v<ResultT, result> && // Not a move/copy ctor
-        // NB: This won't implicitly copy `non_value_result` since the
+        // NB: This won't implicitly copy `error_or_stopped` since the
         // underlying `Expected` is only constructible from `Unexpected`.
         std::is_constructible_v<expected_t, typename ResultT::expected_t &&>)
   /* implicit */ result(Arg&& that)
@@ -615,25 +756,25 @@
   }
 
   /// Retrieve non-reference `T`
-  const T& value_or_throw() const&
+  [[nodiscard]] const T& value_or_throw() const&
     requires(!std::is_reference_v<T>)
   {
     this->throw_if_no_value();
     return *this->exp_;
   }
-  T& value_or_throw() &
+  [[nodiscard]] T& value_or_throw() &
     requires(!std::is_reference_v<T>)
   {
     this->throw_if_no_value();
     return *this->exp_;
   }
-  const T&& value_or_throw() const&&
+  [[nodiscard]] const T&& value_or_throw() const&&
     requires(!std::is_reference_v<T>)
   {
     this->throw_if_no_value();
     return *std::move(this->exp_);
   }
-  T&& value_or_throw() &&
+  [[nodiscard]] T&& value_or_throw() &&
     requires(!std::is_reference_v<T>)
   {
     this->throw_if_no_value();
@@ -642,21 +783,33 @@
 
   /// Retrieve reference `T`.
   ///
-  /// NB Unlike the value-type versions, these can't mutate the reference
-  /// wrapper inside `this`.  Assign a ref-wrapper to `res` to do that.
-  ///
-  /// L-value refs follow `std::reference_wrapper`, exposing the underlying ref
-  /// type regardless of the instance's qualification.  We never add `const`
-  /// for reasons sketched in the test `checkAwaitResumeTypeForRefResult`.
-  T value_or_throw() const&
+  /// NB Unlike the value-type versions, accesors cannot mutate the reference
+  /// wrapper inside `this`.  Assign a ref-wrapper to the `result` to do that.
+
+  /// Lvalue result-ref propagate `const`: `const result<T&>` -> `const T&`.
+  /// See a discussion of the trade-offs in `docs/result.md` & `design_notes.md`
+  [[nodiscard]] like_t<const int&, T> value_or_throw() const&
     requires std::is_lvalue_reference_v<T>
   {
     this->throw_if_no_value();
+    return std::as_const(this->exp_->get());
+  }
+  [[nodiscard]] T value_or_throw() &
+    requires std::is_lvalue_reference_v<T>
+  {
+    this->throw_if_no_value();
     return this->exp_->get();
   }
+  [[nodiscard]] T value_or_throw() &&
+    requires std::is_lvalue_reference_v<T>
+  {
+    this->throw_if_no_value();
+    return this->exp_->get();
+  }
+
   // R-value refs follow `folly::rvalue_reference_wrapper`.  They model
   // single-use references, and thus require `&&` qualification.
-  T value_or_throw() &&
+  [[nodiscard]] T value_or_throw() &&
     requires std::is_rvalue_reference_v<T>
   {
     this->throw_if_no_value();
@@ -664,10 +817,16 @@
   }
 };
 
+template <typename T>
+result(std::reference_wrapper<T>) -> result<T&>;
+
+template <typename T>
+result(rvalue_reference_wrapper<T>) -> result<T&&>;
+
 // Specialization for `T = void` aka `result<>`.
 template <>
-class FOLLY_NODISCARD [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] result<void>
-    final : public detail::result_crtp<result<void>, void> {
+class [[nodiscard]] [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] result<void> final
+    : public detail::result_crtp<result<void>, void> {
  private:
   using base = detail::result_crtp<result<void>, void>;
 
@@ -689,259 +848,47 @@
 template <typename T>
 struct is_result<result<T>> : std::true_type {};
 
-// This short-circuiting coroutine implementation was modeled on
-// `folly/Expected.h`, which is likely to follow the state of the art in
-// compiler support & optimizations.  So, if you're looking at this, please
-// compare it to the original, and backport any improvements here.
-namespace detail {
-
-template <typename>
-struct result_promise_base;
-
-template <typename T>
-struct result_promise_return {
-  result<T> storage_{expected_detail::EmptyTag{}};
-  result<T>*& pointer_;
-
-  /* implicit */ result_promise_return(result_promise_base<T>& p) noexcept
-      : pointer_{p.value_} {
-    pointer_ = &storage_;
-  }
-  result_promise_return(result_promise_return const&) = delete;
-  void operator=(result_promise_return const&) = delete;
-  result_promise_return(result_promise_return&&) = delete;
-  void operator=(result_promise_return&&) = delete;
-  // letting dtor be trivial makes the coroutine crash
-  // TODO: fix clang/llvm codegen
-  ~result_promise_return() {}
-
-  /* implicit */ operator result<T>() {
-    // D42260201: handle both deferred and eager return-object conversion
-    // behaviors see docs for detect_promise_return_object_eager_conversion
-    if (coro::detect_promise_return_object_eager_conversion()) {
-      assert(storage_.is_expected_empty());
-      return result<T>{expected_detail::EmptyTag{}, pointer_}; // eager
-    } else {
-      assert(!storage_.is_expected_empty());
-      return std::move(storage_); // deferred
-    }
-  }
-};
-
-template <typename T>
-struct result_promise_base {
-  result<T>* value_ = nullptr;
-
-  result_promise_base() = default;
-  result_promise_base(result_promise_base const&) = delete;
-  void operator=(result_promise_base const&) = delete;
-  result_promise_base(result_promise_base&&) = delete;
-  void operator=(result_promise_base&&) = delete;
-  ~result_promise_base() = default;
-
-  FOLLY_NODISCARD std::suspend_never initial_suspend() const noexcept {
-    return {};
-  }
-  FOLLY_NODISCARD std::suspend_never final_suspend() const noexcept {
-    return {};
-  }
-  void unhandled_exception() noexcept {
-    // We're making a `result`, so it's OK to forward all exceptions into it,
-    // including `OperationCancelled`.
-    *value_ = non_value_result::make_legacy_error_or_cancellation(
-        exception_wrapper{std::current_exception()});
-  }
-
-  result_promise_return<T> get_return_object() noexcept { return *this; }
-};
-
-template <typename T>
-struct result_promise<T, typename std::enable_if<!std::is_void_v<T>>::type>
-    : public result_promise_base<T> {
-  // For reference types, this deliberately requires users to `co_return`
-  // one of `std::ref`, `std::cref`, or `folly::rref`.
-  //
-  // The default for `U` is tested in `returnImplicitCtor`.
-  template <typename U = T>
-  void return_value(U&& u) {
-    auto& v = *this->value_;
-    expected_detail::ExpectedHelper::assume_empty(v.exp_);
-    v = static_cast<U&&>(u);
-  }
-};
-
-template <typename T>
-struct result_promise<T, typename std::enable_if<std::is_void_v<T>>::type>
-    : public result_promise_base<T> {
-  // When the coroutine uses `return;` you can fail via `co_await err`.
-  void return_void() { this->value_->exp_.emplace(unit); }
-};
-
-template <typename T>
-using result_promise_handle = std::coroutine_handle<result_promise<T>>;
-
-// This is separate to let `result_generator` reuse the awaitables below.
-struct result_await_suspender {
-  // Future: check if all these `FOLLY_ALWAYS_INLINE`s aren't a pessimization.
-  template <typename T, typename U>
-  FOLLY_ALWAYS_INLINE void operator()(T&& t, result_promise_handle<U> handle) {
-    auto& v = *handle.promise().value_;
-    expected_detail::ExpectedHelper::assume_empty(v.exp_);
-    // `T` can be `non_value_result&&`, or one of a few `result<T>` refs.
-    if constexpr (std::is_same_v<non_value_result, std::remove_cvref_t<T>>) {
-      v.exp_ = Unexpected{std::forward<T>(t)};
-    } else {
-      v.exp_ = Unexpected{std::forward<T>(t).non_value()};
-    }
-    // Abort the rest of the coroutine. resume() is not going to be called
-    handle.destroy();
-  }
-};
-
-// There's no `result` in the name as a hint to lift this to a shared header as
-// soon as another usecase arises.
-template <typename AwaitSuspender>
-struct non_value_awaitable {
-  non_value_result non_value_;
-
-  constexpr std::false_type await_ready() const noexcept { return {}; }
-  [[noreturn]] void await_resume() {
-    compiler_may_unsafely_assume_unreachable();
-  }
-  FOLLY_ALWAYS_INLINE void await_suspend(auto h) {
-    AwaitSuspender()(std::move(non_value_), h);
-  }
-};
-
-template <typename T, typename AwaitSuspender>
-struct result_owning_awaitable {
-  result<T> storage_;
-
-  bool await_ready() const noexcept { return storage_.has_value(); }
-  drop_unit_t<T> await_resume() { return std::move(storage_).value_or_throw(); }
-  FOLLY_ALWAYS_INLINE void await_suspend(auto h) {
-    AwaitSuspender()(std::move(storage_), h);
-  }
-};
-
-// We won't have a `folly::rvalue_reference_wrapper` counterpart because
-// awaiting rvalue `result`s is handled by `result_owning_awaitable`, which
-// avoids exposing some dangling reference footguns to the user.
-template <
-    typename T,
-    template <typename>
-    class ConstWrapper,
-    typename AwaitSuspender>
-struct result_ref_awaitable {
-  using ResultT = ConstWrapper<result<T>>;
-  constexpr static bool kIsConstRef = !std::is_same_v<ResultT, result<T>>;
-
-  std::reference_wrapper<ResultT> storage_;
-
-  bool await_ready() const noexcept { return storage_.get().has_value(); }
-
-  // Awaiting a ref to `result<Value>` returns a ref to the value.
-  T& await_resume()
-    requires(!std::is_reference_v<T> && !kIsConstRef)
-  {
-    return storage_.get().value_or_throw();
-  }
-  const T& await_resume()
-    requires(!std::is_reference_v<T> && kIsConstRef)
-  {
-    return storage_.get().value_or_throw();
-  }
-  // Awaiting a ref to `result<Reference>` returns the reference itself.
-  T await_resume()
-    requires std::is_reference_v<T>
-  {
-    return storage_.get().value_or_throw();
-  }
-
-  FOLLY_ALWAYS_INLINE void await_suspend(auto h) {
-    // We can't move the error even out of a mutable l-value reference to
-    // `result`, because the user isn't counting on `co_await std::ref(m)` to
-    // mutate the `result`.
-    AwaitSuspender()(storage_.get(), h);
-  }
-};
-
-} // namespace detail
-
-// co_await stopped_result
-inline auto /* implicit */ operator co_await(stopped_result_t s) {
-  return detail::non_value_awaitable<detail::result_await_suspender>{
-      .non_value_ = non_value_result{s}};
-}
-
-// co_await std::move(res).non_value()
-//
-// Pass-by-&& to discourage accidental copies of `std::exception_ptr`.
-inline auto /* implicit */ operator co_await(non_value_result && nvr) {
-  return detail::non_value_awaitable<detail::result_await_suspender>{
-      .non_value_ = std::move(nvr)};
-}
-
-// co_await resultFunc()
-//
-// DO NOT add a copyable overload for small, trivially copyable types,
-// since this is (a) rare, (b) will make the error path slower.  See the
-// discussion of `co_await std::{move,ref,cref}` in `result.md`.
-template <typename T>
-auto /* implicit */ operator co_await(result<T>&& r) {
-  return detail::result_owning_awaitable<T, detail::result_await_suspender>{
-      .storage_ = std::move(r)};
-}
-
-// co_await std::ref(resultVal)
-template <typename T>
-auto /* implicit */ operator co_await(std::reference_wrapper<result<T>> rr) {
-  return detail::result_ref_awaitable<
-      T,
-      std::type_identity_t,
-      detail::result_await_suspender>{.storage_ = std::move(rr)};
-}
-
-// co_await std::cref(resultVal)
-template <typename T>
-auto /* implicit */ operator co_await(
-    std::reference_wrapper<const result<T>> cr) {
-  return detail::
-      result_ref_awaitable<T, std::add_const_t, detail::result_await_suspender>{
-          .storage_ = std::move(cr)};
-}
-
 /// Wraps the return value from the lambda `fn` in a `result`, putting any
 /// thrown exception into its "error" state.
 ///
 ///   return result_catch_all([&](){ return riskyWork(); });
 ///
-/// Useful when you need a subroutine **definitely** not to throw. In contrast:
-///   - `result<>` coroutines catch unhandled exceptions, but can throw due to
-///     argument copy/move ctors, or due to `bad_alloc`.
+/// Note:
+///   - `result<>` coroutines catch unhandled exceptions, but can additionally
+///     throw `bad_alloc` for coro frame allocations (but see LLVM PR 152623).
 ///   - Like all functions, `result<>` non-coroutines let exceptions fly.
-template <typename F>
+template <typename F, typename RetF = decltype(FOLLY_DECLVAL(F&&)())>
 // Wrap the return type of `fn` with `result` unless it already is `result`.
-typename std::conditional_t<
-    is_instantiation_of_v<result, std::invoke_result_t<F>>,
-    std::invoke_result_t<F>,
-    result<std::invoke_result_t<F>>>
+conditional_t<is_instantiation_of_v<result, RetF>, RetF, result<RetF>>
 result_catch_all(F&& fn) noexcept {
   try {
-    if constexpr (std::is_void_v<std::invoke_result_t<F>>) {
+    if constexpr (std::is_void_v<RetF>) {
       static_cast<F&&>(fn)();
       return {};
     } else {
       return static_cast<F&&>(fn)();
     }
   } catch (...) {
-    // We're a making `result`, so it's OK to forward all exceptions into it,
-    // including `OperationCancelled`.
-    return non_value_result::make_legacy_error_or_cancellation(
-        exception_wrapper{std::current_exception()});
+    return error_or_stopped::from_current_exception();
   }
 }
 
+std::ostream& operator<<(std::ostream&, const error_or_stopped&);
+
 } // namespace folly
 
+template <>
+struct fmt::formatter<folly::error_or_stopped> {
+  constexpr format_parse_context::iterator parse(format_parse_context& ctx) {
+    return ctx.begin();
+  }
+  format_context::iterator format(
+      const folly::error_or_stopped& eos, format_context& ctx) const {
+    eos.format_to(ctx.out());
+    return ctx.out();
+  }
+};
+
 #endif // FOLLY_HAS_RESULT
+
+#undef FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE
diff --git a/folly/folly/result/rich_error.h b/folly/folly/result/rich_error.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_error.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/lang/Exception.h>
+#include <folly/lang/Pretty.h>
+#include <folly/result/detail/rich_error_common.h>
+#include <folly/result/rich_error_fwd.h>
+
+#if FOLLY_HAS_RESULT
+
+/// See: docs/rich_error.md
+
+namespace folly {
+
+/// Syntax sugar for `folly::get_exception<rich_error_base>()`.
+///
+/// This retrieves the **underlying** rich error.  `epitaphs.md`
+/// describes this in detail, but in brief:
+///   - Epitaphs only work with `folly/result/` error containers.
+///   - If the underlying error is not a `rich_error_base`, you will get back
+///     null, **even if** epitaphs were added to it later.
+///   - Furthermore, if you used `epitaph()` or another epitaph
+///     wrapper, `->partial_message()` will NOT give you the epitaph
+///     message, but the rather the one from the base rich error.
+///
+/// Very rarely, you may want `get_outer_exception` to access the epitaph
+/// wrapper object itself.
+inline constexpr get_exception_fn<rich_error_base> get_rich_error{};
+
+// See: docs/rich_error.md
+template <typename UserBase> // must derive from `rich_error_base`
+class [[nodiscard]] rich_error final
+    : public detail::rich_error_with_partial_message<UserBase>,
+      public std::exception {
+ private:
+  using Base = detail::rich_error_with_partial_message<UserBase>;
+
+  void only_rich_error_may_instantiate(
+      rich_error_base::only_rich_error_may_instantiate_t) override {}
+
+ public:
+  const char* what() const noexcept override {
+    // These assertions are under `what()` since this virtual member **must** be
+    // instantiated together with the class template. Has a manual test.
+    detail::static_assert_is_valid_rich_error_type<UserBase>(this);
+    auto msg = Base::partial_message();
+    return msg[0] ? msg : pretty_name<rich_error>();
+  }
+
+  rich_error() = default;
+
+  // Delegate non-standard ctors to the user-defined `Base`
+  template <typename T, typename... Ts>
+    requires(
+        !std::is_same_v<rich_error, std::remove_cvref_t<T>> &&
+        !std::is_same_v<detail::immortal_rich_error_private_t, T>)
+  explicit rich_error(T&& t, Ts&&... ts)
+      : Base{static_cast<T&&>(t), static_cast<Ts&&>(ts)...} {}
+
+  rich_error(detail::immortal_rich_error_private_t, const Base& that)
+      : Base{that} {}
+
+  // This isn't `Base` because `immortal_rich_error` will wrap it again.
+  using folly_detail_base_of_rich_error = UserBase;
+  // NB: This hint is a no-op, unless `UserBase` declares one, in which case
+  // this just prevents the use of the wrong hint.
+  using folly_get_exception_hint_types = tag_t<rich_error>;
+};
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/rich_error_base.cpp b/folly/folly/result/rich_error_base.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_error_base.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/result/rich_error_base.h>
+#include <folly/result/rich_exception_ptr.h>
+
+#include <folly/lang/SafeAssert.h>
+
+#include <ostream>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+source_location rich_error_base::source_location() const noexcept {
+  // The content of the default-constructed class is unspecified by the
+  // standard, but ...  libstdc++, libc++, and MSVC all default to an empty
+  // name.  So, rather than make this `std::optional`, our `format_to` omits
+  // the location if `sl.file_name() != source_location{}.file_name()`.
+  return {};
+}
+
+const rich_exception_ptr* rich_error_base::next_error_for_epitaph()
+    const noexcept {
+  return nullptr;
+}
+
+std::ostream& operator<<(std::ostream& os, const rich_error_base& e) {
+  // NOLINTNEXTLINE(facebook-hte-DetailCall)
+  return detail::ostream_write_via_fmt(os, e);
+}
+
+void rich_error_base::format_to(fmt::appender& out) const {
+  auto msg = partial_message();
+  fmt::format_to(out, "{}", msg);
+  auto fmt_codes = all_codes_for_fmt(msg[0] ? " - " : "");
+  fmt::format_to(out, "{}", fmt_codes);
+  auto sl = source_location();
+  // See `rich_error_base::source_location()` for the rationale
+  if (sl.file_name() != folly::source_location{}.file_name()) {
+    // Omit the separator if the previous steps had no output.
+    if (msg[0] || fmt_codes.saw_code_) {
+      fmt::format_to(out, " @ ");
+    }
+    fmt::format_to(out, "{}:{}", sl.file_name(), sl.line());
+  }
+}
+
+namespace {
+
+struct FormatterState {
+  // Skips potentially-RTTI "is rich?" check when we have a non-rich error.
+  detail::format_to_skip_rich_t skip_rich_;
+
+  // Multiple stacked epitaph wrappers can share the same underlying error,
+  // but we only want to show that underlying error once.
+  const rich_exception_ptr* seen_underlying_rep_ = nullptr;
+  // Deduplicating `rich_exception_ptr*` is not enough -- when printing an
+  // underlying `rich_error_base*`, there might NOT EXIST an original
+  // `rich_exception_ptr*` to compare against.
+  const rich_error_base* seen_underlying_rex_ = nullptr;
+  bool need_after_separator_ = false;
+
+  void mark_underlying_as_seen(
+      const rich_exception_ptr* rep, const rich_error_base* rex) {
+    seen_underlying_rep_ = rep;
+    seen_underlying_rex_ = rex;
+  }
+
+  void format_separator_if_needed(fmt::appender& out) {
+    if (need_after_separator_) {
+      fmt::format_to(out, " [after] ");
+      need_after_separator_ = false;
+    }
+  }
+
+  void format_rich(fmt::appender& out, const rich_error_base* rex) {
+    format_separator_if_needed(out);
+    rex->format_to(out);
+  }
+
+  void format_non_rich(fmt::appender& out, const rich_exception_ptr& rep) {
+    format_separator_if_needed(out);
+    rep.format_to(out, skip_rich_);
+  }
+
+  // Handle an epitaph wrapper -- outputs "underlying [via] wrapper"
+  void format_underlying_and_epitaph(
+      fmt::appender& out, const rich_error_base* wrapper) {
+    auto* underlying_rep = wrapper->underlying_error();
+    // Format the underlying error first, if we haven't already
+    if (seen_underlying_rep_ != underlying_rep) {
+      if (auto* underlying_rex =
+              underlying_rep->get_outer_exception<rich_error_base>()) {
+        if (seen_underlying_rex_ != underlying_rex) {
+          format_rich(out, underlying_rex);
+        }
+        mark_underlying_as_seen(underlying_rep, underlying_rex);
+      } else {
+        format_non_rich(out, *underlying_rep);
+        mark_underlying_as_seen(underlying_rep, nullptr);
+      }
+      fmt::format_to(out, " [via] ");
+    }
+    format_rich(out, wrapper);
+  }
+
+  void format_epitaph_stack_entry(
+      fmt::appender& out, const rich_error_base* current) {
+    if (current->underlying_error()) { // Epitaph wrapper
+      format_underlying_and_epitaph(out, current);
+    } else if (seen_underlying_rex_ != current) { // Underlying error
+      format_rich(out, current);
+      mark_underlying_as_seen(nullptr, current);
+    } // Else: underlying was already formatted
+  }
+
+  // Precondition: `current` was already formatted.
+  void format_rest_of_chain(fmt::appender& out, const rich_error_base* rex) {
+    while (const auto* next_rep = rex->next_error_for_epitaph()) {
+      rex = next_rep->get_outer_exception<rich_error_base>();
+      need_after_separator_ = true;
+      if (!rex) { // Non-rich, making it both last AND underlying.
+        if (seen_underlying_rep_ != next_rep) {
+          format_non_rich(out, *next_rep);
+        }
+        return;
+      }
+      format_epitaph_stack_entry(out, rex);
+    }
+  }
+};
+
+} // namespace
+
+// Format this epitaph stack, starting with its underlying error.
+// Outputs: "Underlying [via] OuterWrapper [after] InnerWrapper"
+void rich_error_base::format_with_epitaphs(fmt::appender& out) const {
+  FormatterState state{detail::format_to_skip_rich_t{true}};
+  state.format_epitaph_stack_entry(out, this);
+  state.format_rest_of_chain(out, this);
+}
+
+// Format only the epitaph stack (caller had already formatted underlying).
+// Precondition: `this` is a wrapper, not an underlying error.
+// Outputs: " [via] OuterWrapper [after] InnerWrapper"
+void rich_error_base::format_with_epitaphs_without_first_underlying(
+    fmt::appender& out) const {
+  FormatterState state{detail::format_to_skip_rich_t{true}};
+  { // Mark the first underlying as already printed (by the caller)
+    auto* rep = underlying_error();
+    FOLLY_SAFE_DCHECK(rep); // See precondition; falls back to `rex == this`
+    state.mark_underlying_as_seen(
+        rep, rep ? rep->get_outer_exception<rich_error_base>() : this);
+  }
+  // By the precondition, `this` wraps the already-printed underlying.
+  fmt::format_to(out, " [via] ");
+  this->format_to(out);
+  state.format_rest_of_chain(out, this);
+}
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/rich_error_base.h b/folly/folly/result/rich_error_base.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_error_base.h
@@ -0,0 +1,612 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/CppAttributes.h>
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/Traits.h>
+#include <folly/Unit.h>
+#include <folly/Utility.h> // FOLLY_DECLVAL
+#include <folly/lang/Pretty.h>
+#include <folly/portability/SourceLocation.h>
+#include <folly/result/rich_error_fwd.h>
+
+#include <iosfwd>
+#include <iterator>
+#include <fmt/core.h>
+
+// Old `fmt` isn't actually supported, but ... it needs to build :(
+#if FMT_VERSION < 80000
+namespace fmt {
+using appender = std::back_insert_iterator<fmt::internal::buffer<char>>;
+template <typename T>
+struct is_formattable;
+} // namespace fmt
+#endif
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+class rich_error_code_query;
+
+namespace detail {
+class epitaph_non_value;
+template <typename>
+class immortal_rich_error_storage;
+template <typename>
+class rich_error_test_for_partial_message;
+template <typename, typename>
+class rich_exception_ptr_impl;
+
+} // namespace detail
+
+// As per `docs/rich_error.md`, user rich error types should use this alias to
+// specify fast exception lookup hints.  This alias is preferred to `tag_t<...>`
+// for brevity, clarity, and forward-compatibility with future optimizations.
+//
+// This takes a pack because a base type may want to hint at the likely derived
+// types.  Hints are checked linearly left-to-right, so keep the list short (<
+// 5), or performance may degrade to be no-better-than-RTTI.
+//
+//   struct Base : rich_error_base {
+//     // hint the base last, since it is rarely used directly
+//     using folly_get_exception_hint_types = rich_error_hints<Derived, Base>;
+//   };
+//   struct Derived : Base {
+//     using folly_get_exception_hint_types = rich_error_hints<Derived>;
+//   };
+//
+// Implementation note: Do not hint the bare `ThisAndLikelyDerivedTypes`, or
+// `immortal_rich_error_storage<...>`, since we never make
+// `std::exception_ptr`s with either of those, only with `rich_error<...>`.
+template <typename... ThisAndLikelyDerivedTypes>
+using rich_error_hints = tag_t<rich_error<ThisAndLikelyDerivedTypes>...>;
+
+/// The main API for `rich_error<...>` types.  Prefer to use `get_rich_error()`
+/// to get pointers to this interface -- its "happy path" avoids RTTI costs.
+/// Or, when `rich_error.h` isn't included, `get_exception<rich_error_base>()`.
+///
+/// To define a rich error type `T`, derive from `rich_error_base` or a
+/// descendant, and add:
+///   using folly_get_exception_hint_types = rich_error_hints<T>;
+///
+/// Then, construct instances via `rich_error<T>` or `immortal_rich_error<T,
+/// ...>`. By convention, most errors provide `rich_error<T> T::make()` static
+/// factories.
+///
+/// `coded_rich_error.h` is like `std::system_error`, but cheaper and with more
+/// functionality.  It stores a user-specified code & message, and may nest
+/// other "caused-by" exceptions underneath itself.
+///
+/// `underlying_error()` is non-virtual to speed up error checks --
+/// `get_exception<Ex>(rich_exception_ptr)` skips to the "underlying" error.
+/// This sped up almost all affected `rich_exception_ptr` benchmarks.
+class rich_error_base {
+ private:
+  template <typename>
+  friend class rich_error; // may instantiate us
+  template <typename>
+  friend class detail::immortal_rich_error_storage; // may instantiate us
+  template <typename>
+  friend class detail::rich_error_test_for_partial_message; // test trait
+
+  // The member forces all rich errors to be instantiated through one of these
+  // "leaf" error objects:
+  //   - `rich_error<UserBase>`
+  //   - `immortal_rich_error<UserBase, ...>`
+  //
+  // This is important for several reasons:
+  //
+  //   - Consistent UX: both `get_exception<rich_error<UserBase>>(rep)` and
+  //     `get_exception<UserBase>(rep)` should work, regardless of whether the
+  //     `rich_exception_ptr` points at a dynamic or an immortal error.  The
+  //     catch is that immortals must be constexpr, but `std::exception` is not
+  //     `constexpr` until C++26.  Adding it as a base of the leaf
+  //     `rich_error<UserBase>` works around this issue.
+  //
+  //   - Object-slicing safety, while letting rich errors be movable.  Compared
+  //     to in-place, constructing regular classes is simple.  Movability keeps
+  //     costs low -- e.g. it avoids `exception_shared_string` atomic ops.
+  //
+  //     Here is a canonical slicing bug: moving a base-class subobject from a
+  //     derived instance, which most likely invalidates the latter.  The
+  //     derived-class state may have depended on the base-class subobject
+  //     state and is not prepared for it to have been stolen.
+  //
+  //   - As a bonus, this lets us verify `folly_get_exception_hint_types`
+  //     for all rich errors.  Today, this helps maintain a consistently fast
+  //     "happy path" lookup performance.  In the future, this hook can help
+  //     implement fast RTTI queries as in `docs/future_fast_rtti.md`.
+  //
+  // The passkey stops derived classes from overriding the private member.
+  struct only_rich_error_may_instantiate_t {};
+  virtual void only_rich_error_may_instantiate(
+      only_rich_error_may_instantiate_t) = 0;
+
+  // This is set only by `epitaph_non_value`.  All reads should go through
+  // `...underlying_error()` to make risky mutable access more obvious.
+  //
+  // It is accessed on each `get_exception<Ex>(rich_exception_ptr)`, and by
+  // member functions that use the traversal `get_underlying()`.  There's a
+  // noticeable speedup from avoiding a vtable dispatch for this.  It's
+  // especially noticeable in `get_rich_error()`, saving as much as 50% for
+  // dynamic, known-`rich_exception_base` pointers.  There are smaller but still
+  // noticeable gains throughout the benchmark.
+  rich_exception_ptr* underlying_ptr_{};
+
+ protected: // Can only construct via `rich_error<>` / `immortal_rich_error<>`
+  constexpr rich_error_base() = default;
+
+  rich_error_base(const rich_error_base&) = default;
+  rich_error_base(rich_error_base&&) = default;
+  rich_error_base& operator=(const rich_error_base&) = default;
+  rich_error_base& operator=(rich_error_base&&) = default;
+
+ public:
+  virtual ~rich_error_base() = default;
+
+  virtual folly::source_location source_location() const noexcept;
+
+  // Use `<<` or `fmt` to log errors, AVOID `partial_message()` & `what()`.
+  //   - `<<` and `fmt` render more information than this partial message:
+  //     epitaphs, exception-specific data, source location.
+  //   - With care, you can avoid heap allocations for the complete message.
+  //
+  // Most higher-level errors in `folly/result/` implement this for you -- see
+  // e.g. `coded_rich_error` and `epitaph.h`. If no base supplies it,
+  // `rich_error` and `immortal_rich_error` will automatically back-fill this
+  // with `pretty_name` of the base.
+  //
+  // `rich_error` implements `what()` in terms of `partial_message()`.
+  // An implementation needing a dynamic `what()` should strongly consider
+  // `exception_shared_string` as storage, since that efficiently stores a
+  // string literal pointer OR a heap-allocated refcounted string.
+  //
+  // Design note: This only exists to implement `std::exception::what()`. That
+  // unfortunate API forces one of several poor choices:
+  //  - Only support literal string messages.
+  //  - Use a dynamic allocation to eagerly pre-format the full message when
+  //    the error happens, whether it'll be used or not.
+  //  - Do some atomic trickery to make the allocation & format step lazy.
+  // In contrast, rich error `fmt` support is lazy & costs nothing up-front.
+  // Storing only structured data & literal strings, they are cheap to make.
+  virtual const char* partial_message() const noexcept = 0;
+
+  // Override this to support RTTI-free `get_rich_error_code()`. See the
+  // `rich_error_bases_and_own_codes` doc for how to implement this.
+  //
+  // Briefly: The query contains an ABI-stable UUID for a rich error code type
+  // `C`.  If the error has a code of that type, this call undoes the type
+  // erasure.  The returned query is then updated so the user can efficiently
+  // get the value type `C`.  There's also a special `kFormatterUuid`, which
+  // instead returns a callback that formats ALL the codes of this error.
+  constexpr virtual void retrieve_code(rich_error_code_query&) const {}
+
+  // Returns a `fmt`-formattable object that renders all the available codes in
+  // this error's inheritance hierarchy, like so: "Specific=1, General=2".
+  //
+  //   const rich_error_base& err = ...;
+  //   auto s = fmt::format("code={}", err.all_codes_for_fmt());
+  class fmt_all_codes_t {
+   private:
+    friend class rich_error_base;
+    friend struct fmt::formatter<folly::rich_error_base::fmt_all_codes_t>;
+    const rich_error_base& error_ref_;
+    const char* pre_separator_;
+    bool saw_code_{false}; // formatted at least one code?
+    constexpr fmt_all_codes_t(
+        const rich_error_base& e [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]],
+        const char* pre_separator)
+        : error_ref_(e), pre_separator_(pre_separator) {}
+  };
+  fmt_all_codes_t all_codes_for_fmt(const char* pre_separator = "") const
+      [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] {
+    return fmt_all_codes_t{*this, pre_separator};
+  }
+
+  // Rendering for rich errors via `fmt` and `ostream<<`.
+  virtual void format_to(fmt::appender& out) const;
+
+  // Format this epitaph stack, starting with its underlying error.
+  void format_with_epitaphs(fmt::appender& out) const;
+
+  // Format only the epitaph stack, omitting the first underlying error.
+  // Precondition: `this` is a wrapper, not an underlying error.
+  void format_with_epitaphs_without_first_underlying(fmt::appender& out) const;
+
+  // Formatting of rich errors follows the `next_error_for_epitaph()`
+  // linked list, printing each one in turn.  There are two use-cases:
+  //
+  // (1) `epitaph` -- as you stack these, the outer one always points
+  // at the next one, etc.  But the `underlying_error()` for all of them points
+  // at the original error being propagated. The log output will be like this:
+  //
+  //   OriginalErr [via] last annotation @ src.cpp:50 [after] first @ src.cpp:40
+  //
+  // Here, "via" means "what follows is an epitaph stack for the error", and
+  // "after" separates entries in that stack.
+  //
+  // (2) To emulate `std::nested_exception` (but much cheaper to log!), any
+  // rich error type may internally store `rich_exception_ptr next_`, and
+  // expose that via `next_error_for_epitaph()`.  An example is
+  // provided in `nestable_coded_rich_error.h`.  For example, if `OriginalErr`
+  // from (1) had wrapped `NestedErr`, which was turn wrapped by its own
+  // `epitaph` during propagation, then we might see this output:
+  //
+  //   OriginalErr [via] last annotation @ src.cpp:50 [after] first @ src.cpp:40
+  //   [after] NestedErr [via] nested_src.cpp:12
+  virtual const rich_exception_ptr* next_error_for_epitaph() const noexcept;
+
+  // Used only by "transparent" error wrappers like `epitaph()`.
+  // Otherwise, `nullptr`, meaning that `this` itself is the underlying error.
+  //
+  // From a program-logic perspective, `underlying_error()` is the error that
+  // is actually propagating.  To observe anything about `this`, the outer
+  // error object, the end-user would have to call `get_outer_exception`.
+  //
+  // Every epitaph wrapper points this at the original error, so that
+  // `get_exception<Ex>()` is O(1).
+  constexpr const rich_exception_ptr* underlying_error() const noexcept {
+    return underlying_ptr_;
+  }
+  class underlying_error_private_t {
+    friend class detail::epitaph_non_value; // Sets `underlying_ptr_`
+    // Needs mutable access for `get_mutable_exception`.
+    template <typename, typename>
+    friend class detail::rich_exception_ptr_impl;
+    underlying_error_private_t() = default;
+  };
+  // This is private because allowing mutable access to the `underlying_ptr_`
+  // is very risky.  It is only used to implement `get_mutable_exception`,
+  // which does NOT mutate the underlying REP, but only the pointed-to
+  // exception object.  This distinction is important for wrappers because
+  // `*underlying_ptr_` aka `epitaph_non_value::next_` ends up being
+  // unexpectectedly shared state.  Consider:
+  //   - `repConst` and `repMutable` both point to an `epitaph_non_value`
+  //     object, call it `e`.  It sets `underlying_ptr_` to point to its
+  //     `next_` member.
+  //   - A function calls `repMutable.with_underlying()` or
+  //     `mutable_underlying_error()` and gets `e.next_`.
+  //   - If that is a non-`const` pointer, the function can now mutate the
+  //     pointed-to `rich_exception_ptr`, and suddenly the underlying error
+  //     object of `repConst` also changes -- but the identity of a wrapped
+  //     error object should NOT change!
+  // Gate this API since this kind of aliasing bug is subtle AND dangerous.
+  constexpr rich_exception_ptr* mutable_underlying_error(
+      underlying_error_private_t) noexcept {
+    return underlying_ptr_;
+  }
+
+ protected:
+  // Only used by `epitaph_non_value`.  It can't set `underlying_ptr_` until
+  // AFTER its `next_` is populated, so this is a setter, not a ctor argument.
+  // Do NOT add more callsites without maintainer review -- the safer design
+  // might be to add an immovable base class that exposes the setter.
+  void set_underlying_error(
+      underlying_error_private_t, rich_exception_ptr* ptr) noexcept {
+    underlying_ptr_ = ptr;
+  }
+};
+
+/// `rich_error_base` is `fmt` formattable (below), but also has this sugar for
+/// writing rich errors to glog & `std` streams.  This ought to be more robust
+/// under OOM than `stream << fmt::format("{}", err)`, since `fmt` may allocate.
+std::ostream& operator<<(std::ostream&, const rich_error_base&);
+
+template <typename Ex>
+class rich_ptr_to_underlying_error;
+
+namespace detail {
+template <auto, typename Ex>
+void expectGetExceptionResult(const rich_ptr_to_underlying_error<Ex>&);
+} // namespace detail
+
+// Quacks like `Ex*`, but show an epitaph stack when formatted.
+// Returned by `get_exception<Ex>(rep)` and `get_mutable_exception<Ex>(rep)`,
+// for `rich_exception_ptr<...> rep`.  The respective `get_exception()`
+// implementations have more detailed docs.
+template <typename Ex>
+class rich_ptr_to_underlying_error {
+ private:
+  Ex* raw_ptr_{nullptr};
+  const rich_error_base* top_rich_error_{nullptr};
+
+  friend struct fmt::formatter<rich_ptr_to_underlying_error>;
+  template <auto, typename T>
+  friend void detail::expectGetExceptionResult(
+      const rich_ptr_to_underlying_error<T>&);
+
+ protected:
+  template <typename, typename>
+  friend class detail::rich_exception_ptr_impl;
+  constexpr rich_ptr_to_underlying_error(Ex* p, const rich_error_base* top)
+      : raw_ptr_(p), top_rich_error_(top) {}
+
+ public:
+  // `get_exception<Ex>(result)` needs this.
+  explicit constexpr rich_ptr_to_underlying_error(std::nullptr_t) {}
+
+  // Immovable for now, since the primary use-case is just:
+  //   if (auto ex = get_exception<Ex>(rich_eptr)) { /*...*/ }
+  // Escape hatch ideas:
+  //   - `Ex* get()` below
+  //   - to delegate formatting to helper func, pass by `auto&`
+  //
+  // Future: Relax this if you have a compelling reason.  Some redundant
+  // safety comes from the `lifetimebound` annotation on `get_exception`.
+  // But, in clang-17 that doesn't catch some obvious use-after-frees.
+  rich_ptr_to_underlying_error(const rich_ptr_to_underlying_error&) = delete;
+  rich_ptr_to_underlying_error operator=(const rich_ptr_to_underlying_error&) =
+      delete;
+  rich_ptr_to_underlying_error(rich_ptr_to_underlying_error&&) = delete;
+  rich_ptr_to_underlying_error operator=(rich_ptr_to_underlying_error&&) =
+      delete;
+  ~rich_ptr_to_underlying_error() = default;
+
+  constexpr Ex& operator*() const { return *raw_ptr_; }
+  constexpr Ex* operator->() const { return raw_ptr_; }
+  constexpr Ex* get() const { return raw_ptr_; }
+
+  // Conversion to raw pointer lossy.  Make it explicit to avoid accidentally
+  // shedding rich error formatting context -- propagation notes, source
+  // locations, codes, etc.
+  explicit constexpr operator Ex*() const { return raw_ptr_; }
+
+  // Make `if (auto ex = get_exception<...>(...))` work.  Or, use `bool{ex}`
+  // for explicit conversion.  Truly quacking like a raw pointer would make
+  // this implicit, but that has undesirable consequences.  For example, any
+  // common protocol that takes `bool` like `fmt` or `<<(ostream&, bool)` would
+  // treat these as `bool`, unless a more specific match is provided.  In the
+  // future, we could reconsider this trade-off.
+  explicit constexpr operator bool() const { return raw_ptr_; }
+
+  friend constexpr bool operator==(
+      std::nullptr_t, const rich_ptr_to_underlying_error& p) {
+    return p.raw_ptr_ == nullptr;
+  }
+  friend constexpr bool operator==(
+      const Ex* raw_p, const rich_ptr_to_underlying_error& p) {
+    return raw_p == p.raw_ptr_;
+  }
+  friend constexpr bool operator==(
+      const rich_ptr_to_underlying_error& lhs,
+      const rich_ptr_to_underlying_error& rhs) {
+    return lhs.raw_ptr_ == rhs.raw_ptr_;
+  }
+};
+
+// See docs in `rich_error_code.h`
+//
+// DO NOT add your own `rich_error_code<std::errc>` -- use `errc_rich_error.h`.
+template <typename Code>
+struct rich_error_code;
+
+// User-opaque part of the `get_rich_error_code()` machinery. End-users only
+// pass it from their `retrieve_code()` override into the `retrieve_code` impl.
+//
+// Implementation note -- this has 3 roles:
+//  - Forwarding the UUID of the desired `Code` into `retrieve_code()`.
+//  - Stores a nullable, type-erased `Code` for `get_rich_error_code()`.
+//  - Passkey privacy -- ensures the only public entry point to the rich error
+//    code machinery is `get_rich_error_code()`.
+class rich_error_code_query {
+ private:
+  uint64_t uuid_;
+
+ protected:
+  // Stores either a mangled code or a formatter function pointer.
+  // The active member depends on the `uuid_` value:
+  //   Non-reserved: mangled_code_ is active (normal code retrieval)
+  //   kFormatterUuid: formatter_fn_ is active (formatting all codes)
+  union {
+    uintptr_t mangled_code_;
+    bool (*formatter_fn_)( // Returns `true` if > 0 codes were formatted
+        const rich_error_base&, fmt::appender&, const char* pre_separator);
+  };
+
+  // Future: This should use `std::optional`, but we still need this to build
+  // with libstdc++ from GCC 11.2, which lacks a constexpr `emplace`.
+  // https://godbolt.org/z/Y77WfKG94
+  bool has_value_{false};
+
+  // Reserved UUIDs: [0, 100000]
+  static constexpr uint64_t kFormatterUuid = 0; // `fmt` formatting
+  static constexpr uint64_t kMaxReservedUuid = 100000;
+
+  // We don't want to initialize the union data guarded by `has_value_`.
+  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+  explicit constexpr rich_error_code_query(uint64_t uuid) : uuid_{uuid} {}
+
+  constexpr bool uuid_matches(uint64_t uuid) const { return uuid_ == uuid; }
+
+  template <typename, typename, auto...>
+  friend class rich_error_bases_and_own_codes;
+
+  friend struct fmt::formatter<rich_error_base::fmt_all_codes_t>;
+
+  template <typename Code>
+  friend class get_rich_error_code_fn;
+
+ public:
+  // Required for usage where an outer class delegates code retrieval to an
+  // inner one, and provides one in case of miss.
+  bool has_value() const { return has_value_; }
+};
+
+} // namespace folly
+
+// `rich_error_base` AND derived classes are formattable.
+template <>
+struct fmt::formatter<folly::rich_error_base> {
+  constexpr format_parse_context::iterator parse(format_parse_context& ctx) {
+    // FIXME: We don't currently support align/fill/padding, not because it's
+    // impossible, but because `fmt` didn't make it easy -- all the code
+    // related to spec handling, and padded/filled/aligned output of strings is
+    // in `fmt::detail`.  When we do, don't use `fmt::nested_formatter` (this
+    // allocates, which adds unnecessary fragility on an error handling path).
+    // Instead, add a `rich_error_base::formatted_size()` and use that.
+    return ctx.begin();
+  }
+  format_context::iterator format(
+      const folly::rich_error_base& e, format_context& ctx) const {
+    auto it = ctx.out();
+    e.format_with_epitaphs(it);
+    return it;
+  }
+};
+template <typename T>
+  requires std::is_convertible_v<T*, folly::rich_error_base*>
+struct fmt::formatter<T> : fmt::formatter<folly::rich_error_base> {};
+
+// Format pointer-like returned by `get_exception<Ex>(rich_exception_ptr)`.
+// Crucially, this displays `epitaph()` stacks when available.
+//
+template <typename Ex>
+struct fmt::formatter<folly::rich_ptr_to_underlying_error<Ex>> {
+ private:
+  // `Ex` is formattable AND not a `rich_error_base`: format `Ex` first, then
+  // append the rich error formatting.
+  //
+  // Rather than branch a single class on this, it would be cleaner to use
+  // multiple partial specializations with mutually-exclusive `requires`
+  // clauses, but GCC treats those as redefinitions.
+  static constexpr bool kUseExFormatter = fmt::is_formattable<Ex>::value &&
+      !std::is_convertible_v<Ex*, const folly::rich_error_base*>;
+
+  [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] folly::conditional_t<
+      kUseExFormatter,
+      fmt::formatter<std::remove_cv_t<Ex>>,
+      folly::Unit> ex_formatter_;
+
+ public:
+  constexpr format_parse_context::iterator parse(format_parse_context& ctx) {
+    if constexpr (kUseExFormatter) {
+      return ex_formatter_.parse(ctx);
+    } else {
+      return ctx.begin();
+    }
+  }
+
+  format_context::iterator format(
+      const folly::rich_ptr_to_underlying_error<Ex>& p,
+      format_context& ctx) const {
+    using UncvEx = std::remove_cv_t<Ex>;
+    auto it = ctx.out();
+    if (p.raw_ptr_ == nullptr) {
+      fmt::format_to(it, "[nullptr folly::rich_ptr_to_underlying_error]");
+      return it;
+    }
+    if constexpr (kUseExFormatter) {
+      it = ex_formatter_.format(*p.raw_ptr_, ctx);
+      if (p.top_rich_error_) {
+        // Underlying error already formatted above, just format the wrappers
+        p.top_rich_error_->format_with_epitaphs_without_first_underlying(it);
+      }
+      return it;
+    } else {
+      // Use detailed rich-error formatting if available: either we have a
+      // epitaph wrapper, or the underlying error is rich, or both.
+      if (std::is_convertible_v<Ex*, const folly::rich_error_base*> ||
+          p.top_rich_error_) {
+        p.top_rich_error_->format_with_epitaphs(it);
+      } else {
+        // For non-formattable non-rich errors without a wrapper, match the
+        // `rich_error_base::format_to` formatting.
+        if constexpr (std::is_convertible_v<Ex*, const std::exception*>) {
+          fmt::format_to(
+              it, "{}: {}", folly::pretty_name<UncvEx>(), p.raw_ptr_->what());
+        } else {
+          fmt::format_to(it, "{}", folly::pretty_name<UncvEx>());
+        }
+      }
+      return it;
+    }
+  }
+};
+
+namespace folly {
+namespace detail {
+std::ostream& ostream_write_via_fmt(std::ostream& os, const auto& v) {
+  try {
+    os << fmt::format("{}", v);
+  } catch (const std::bad_alloc&) {
+    // Per `ostream_append_simple_rich_error`, ~4.5x slower than `os <<
+    // fmt::format("{}", e);` for small strings, but doesn't use heap.
+    fmt::format_to(std::ostream_iterator<char>(os), "{}", v);
+  }
+  return os;
+}
+} // namespace detail
+
+template <typename Ex>
+std::ostream& operator<<(
+    std::ostream& os, const rich_ptr_to_underlying_error<Ex>& ep) {
+  return detail::ostream_write_via_fmt(os, ep);
+}
+} // namespace folly
+
+// See `rich_error_code::all_codes_for_fmt()` for usage
+template <>
+struct fmt::formatter<folly::rich_error_base::fmt_all_codes_t> {
+  constexpr format_parse_context::iterator parse(format_parse_context& ctx) {
+    return ctx.begin();
+  }
+  format_context::iterator format(
+      folly::rich_error_base::fmt_all_codes_t& err, // this mutates `.saw_code_`
+      format_context& ctx) const {
+    folly::rich_error_code_query q{
+        folly::rich_error_code_query::kFormatterUuid};
+    err.error_ref_.retrieve_code(q);
+    auto out = ctx.out();
+    if (q.has_value_) {
+      err.saw_code_ = q.formatter_fn_(err.error_ref_, out, err.pre_separator_);
+    }
+    return out;
+  }
+};
+
+namespace folly::detail {
+
+// Implementation notes:
+//  - This needs a body because GCC doesn't want `d` referenced in a `->` type
+//    signature.
+//  - To use this with the cheaper-to-compile `FOLLY_DECLVAL`, which is
+//    `nullptr`, this must be in an unevaluated context, since patently-null
+//    static casts are special in that they discard offsets.  So, the below
+//    equality would always be true during constant evaluation.
+template <typename B, typename D>
+inline consteval auto is_offset0_base_of(D d) {
+  return std::bool_constant<
+      static_cast<const void*>(&d) ==
+      static_cast<const void*>(static_cast<const B*>(&d))>{};
+}
+
+// Has `test_has_offset0_base()` in `rich_error_test.cpp`.
+//
+// This looks superficially similar to `is_pointer_interconvertible_base_of_v`,
+// but they differ for multiple & virtual inheritance:
+// https://godbolt.org/z/P8Teoh1zh
+//
+// Future: Similar to `promise_at_offset0`, worth unifying?
+template <typename D, typename B>
+concept has_offset0_base =
+    decltype(is_offset0_base_of<B>(FOLLY_DECLVAL(D)))::value;
+
+} // namespace folly::detail
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/rich_error_code.cpp b/folly/folly/result/rich_error_code.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_error_code.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/result/rich_error_code.h>
+
+#include <folly/lang/Exception.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly::detail {
+
+void retrieve_rich_error_code_from_exception_ptr(
+    const std::exception_ptr& ep, rich_error_code_query& query) {
+  if (ep) {
+    if (auto* ex = exception_ptr_get_object_hint<const rich_error_base>(ep)) {
+      return ex->retrieve_code(query);
+    }
+  }
+}
+
+} // namespace folly::detail
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/rich_error_code.h b/folly/folly/result/rich_error_code.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_error_code.h
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <fmt/core.h>
+
+#include <folly/lang/Pretty.h>
+#include <folly/result/rich_error_base.h>
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+
+#if FOLLY_HAS_RESULT
+
+/// This file provides `get_rich_error_code<Code>(container)` -- both the
+/// generic behaviors, and the `retrieve_code` protocol from `rich_error_base`.
+///
+/// Read `docs/rich_error_code.md` for a user-centric introduction.  These
+/// in-header docs focus on the details of using & extending the machinery.
+///
+/// ## How to register a new error code
+///
+/// To use `Code` with `get_rich_error_code<Code>()`, you must specialize
+/// `rich_error_code<Code> with the following features.
+///
+/// WARNING: It is forbidden to specialize `rich_error_code` for `std` enums
+/// outside of `folly/result` -- for an example, see `errc_rich_error.h`.
+///
+/// (1) Mandatory: Stable UUID.  This becomes part of the ABI, and must not
+/// collide with other codes, so it's best to pick a 64-bit random number:
+///
+///   python3 -c 'import random;print(random.randint(0, 2**64 - 1))'
+///   static constexpr uint64_t uuid = <random>ULL;
+///
+/// Using `Code`s with colliding UUIDs in the same error class will cause a
+/// compile error, forcing an ABI break. UUIDs in [0, 100000] are reserved.
+///
+/// Rationale: UUIDs enable a much-faster-than-RTTI type comparison. This idea
+/// originates in Niall Douglas's wg21.link/P1028.
+///
+/// (2) Encouraged: `fmt::formatter<Code>` specialization, as per `fmt` docs.
+/// May omit if you're ok with enums being printed as the underlying type.
+///
+/// (3) Optional: Code name for `fmt` output. Defaults to `pretty_name<Code>`.
+///
+///   static constexpr const char* name = "Code";
+///
+/// (4) If your code is NOT an enum, you **must** define mangle/unmangle
+/// functions. Be sure to add round-trip tests, and watch out for truncation and
+/// signedness issues.  Add `static_assert`s for platform-specific assumptions.
+///
+///   static constexpr uintptr_t mangle_code(B1 code);
+///   static constexpr B1 unmangle_code(uintptr_t value);
+
+namespace folly {
+
+namespace detail {
+
+// Mangle a `Code` into a type-erased `uintptr_t`
+template <typename Code>
+constexpr uintptr_t mangle_rich_error_code(Code code) {
+  // Enum codes don't need a user-provided mangle function
+  if constexpr (std::is_enum_v<Code>) {
+    auto underlying = to_underlying(code);
+    static_assert(sizeof(underlying) <= sizeof(uintptr_t));
+    // Test for enum conversion: https://godbolt.org/z/Yf8qPznaf
+    return static_cast<uintptr_t>(underlying);
+  } else {
+    return rich_error_code<Code>::mangle_code(code);
+  }
+}
+
+// Unmangle a type-erased `uintptr_t` back to a `Code`
+template <typename Code>
+constexpr Code unmangle_rich_error_code(uintptr_t value) {
+  // Enum codes don't need a user-provided unmangle function
+  if constexpr (std::is_enum_v<Code>) {
+    using Underlying = std::underlying_type_t<Code>;
+    static_assert(sizeof(Underlying) <= sizeof(uintptr_t));
+    return static_cast<Code>(static_cast<Underlying>(value));
+  } else {
+    return rich_error_code<Code>::unmangle_code(value);
+  }
+}
+
+void retrieve_rich_error_code_from_exception_ptr(
+    const std::exception_ptr&, rich_error_code_query&);
+
+// Customization point for `get_rich_error_code<Code>()`.
+//
+// INTERNAL USE ONLY - End-users should NEVER specialize this trait. Instead,
+// implement the `folly::get_exception<Ex>(v)` protocol for your type.
+//
+// This keeps `rich_error_code.h` & `folly/lang/Exception.h` independent.
+//
+// `get_rich_error_code<Code>()` should work with various error containers
+// (`exception_ptr`, `exception_wrapper`, `result`, `rich_exception_ptr`, etc).
+// The uniform way to get `rich_error_base*` out of those is `get_exception`,
+// but we don't want `rich_error_code.h` to include `Exception.h`, or vice
+// versa, and we even want to avoid `Exception.h` -> `rich_error_base.h`.
+//
+// To break the cycle, we add a second -- and last -- specialization of this
+// template in `Exception.h`.
+//
+// ### ODR SAFETY ###
+//
+// Do NOT add or change specializations of this template without reading!
+//
+// In general, there's an ODR risk with declaring the primary template in
+// multiple headers, since different include paths could synthesize different
+// specializations for the same type.
+//
+// However, this specific setup is fine because:
+//   - This is not a user-specializable trait, but a `detail::`.
+//   - This template is ONLY used in `rich_error_code.h`.
+//   - `Exception.h` is the ONLY other file declaring or specializing this
+//     template.  That specialization can only compile when matching types that
+//     speak `get_exception`.  Its participation in ODR violations is not
+//     likely -- all current (and likely future) such types include
+//     `Exception.h`, so the compiler would see conflicting definitions.
+//
+// The present current file specializes for:
+//   - `rich_error_base`, because `Exception.h` shouldn't include its header.
+//   - `std::exception_ptr` because this file is the only way to use the
+//     template, and defining this standard-type specialization here ensures
+//     that any redefinition is a compile error.
+template <typename T, typename /*SFINAE*/>
+struct get_rich_error_code_traits;
+
+// Special case: to avoid ODR issues, must be in this header (details above)
+template <std::same_as<std::exception_ptr> T>
+struct get_rich_error_code_traits<T, void> {
+  static void retrieve_code(const T& t, rich_error_code_query& q) {
+    retrieve_rich_error_code_from_exception_ptr(t, q);
+  }
+};
+
+// Most `get_rich_error_code` variants will delegate to this branch.
+template <std::derived_from<rich_error_base> T>
+struct get_rich_error_code_traits<T, void> {
+  static constexpr void retrieve_code(const T& t, rich_error_code_query& q) {
+    // Here, we can know both `Code` and the error type `T`, so it'd be possible
+    // to explicitly devirtualize the query.  But, if the compiler can see the
+    // body of `retrieve_code`, then it is usually able to pick the right branch
+    // based on the statically-known `q.uuid`.  So, the utility is limited.
+    t.retrieve_code(q);
+  }
+};
+
+} // namespace detail
+
+/// Implements `retrieve_code()` for errors supporting `get_rich_error_code()`.
+///
+/// `CodeFns...` are member function pointers returning error codes belonging to
+/// this class, and not inherited from any base class.
+///
+/// `BaseList` is a type-list of direct base error classes of `Self`.
+///
+/// IMPORTANT: If a base of the class is omitted from this list, its codes will
+/// not be retrievable. We cannot detect this mistake without C++26 reflection.
+///
+/// Example:
+///
+///   struct error_B : error_A {
+///     B1 b1() const { return b1_; }
+///     B2 b2() const { return b2_; }
+///     using folly_rich_error_codes_t = rich_error_bases_and_own_codes<
+///         error_B, tag_t<error_A>, &error_B::b1, &error_B::b2>;
+///     constexpr void retrieve_code(rich_error_code_query& q) const override {
+///       // NB: If you don't need `constexpr`, then you might be able to reduce
+///       // code size & improve build speed by delegating to a private member
+///       // that moves this instantiation to your `.cpp` file.
+///       return folly_rich_error_codes_t::retrieve_code(*this, q);
+///     }
+///   };
+///
+/// Future / perf: If you are able to inline lookup (rare!), and benefit from it
+/// (even rarer!), AND want to avoid bloat from formatting code in headers, the
+/// fix is to add an out-of-line `format_all_codes_recursive` proxy to your
+/// class, and pass it via an `auto` template to `retrieve_code()`.
+template <typename Self, typename BaseList, auto... CodeFns>
+class rich_error_bases_and_own_codes {
+ private:
+  template <typename, typename, auto...>
+  friend class rich_error_bases_and_own_codes;
+
+  template <auto CodeFn>
+  using code_type =
+      std::remove_cvref_t<decltype((FOLLY_DECLVAL(const Self&).*CodeFn)())>;
+
+  template <typename... Bases>
+  static constexpr auto uuids_for_codes_of_bases_and_self(tag_t<Bases...>)
+      -> value_list_concat_t<
+          vtag_t,
+          typename Bases::folly_rich_error_codes_t::all_uuids_t...,
+          vtag_t<rich_error_code<code_type<CodeFns>>::uuid...>>;
+
+  using all_uuids_t = decltype(uuids_for_codes_of_bases_and_self(BaseList{}));
+
+  template <uint64_t... UUIDs>
+  static constexpr bool have_duplicate_or_reserved_uuids(vtag_t<UUIDs...>) {
+    constexpr uint64_t arr[] = {UUIDs...};
+    for (size_t i = 0; i < sizeof...(UUIDs); ++i) {
+      // UUIDs <= 100000 are reserved (e.g. for `fmt` formatting)
+      if (arr[i] <= rich_error_code_query::kMaxReservedUuid) {
+        return true;
+      }
+      for (size_t j = i + 1; j < sizeof...(UUIDs); ++j) {
+        if (arr[i] == arr[j]) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  static_assert(
+      !have_duplicate_or_reserved_uuids(all_uuids_t{}),
+      "The error hierarchy contains a duplicate UUID, or a code with a "
+      "UUID with a reserved value of <= 100000.");
+
+ public:
+  // Retrieve code for a given UUID.
+  //
+  // Future: With C++26 reflection, we could figure out `Bases` from the type of
+  // `self`, preventing the user error where the 2 diverge.
+  template <std::same_as<Self> Self2> // prevent slicing
+  static constexpr void retrieve_code(
+      const Self2& self, rich_error_code_query& query) {
+    // Violating this would break inheritance of codes from `Self`
+    static_assert(
+        std::is_same_v<
+            rich_error_bases_and_own_codes,
+            typename Self2::folly_rich_error_codes_t>);
+
+    // First, check for user-defined codes from this class (not inherited)
+    ([&]<auto CodeFn>(vtag_t<CodeFn>) {
+      // Call the function whose `Code` matches the requested UUID.
+      if (query.uuid_matches(rich_error_code<code_type<CodeFn>>::uuid)) {
+        query.mangled_code_ = detail::mangle_rich_error_code((self.*CodeFn)());
+        query.has_value_ = true;
+        return true;
+      }
+      return false;
+    }(vtag<CodeFns>) ||
+     ...) ||
+        // Then, delegate to base classes
+        [&]<typename... Bases>(tag_t<Bases...>) {
+          return (
+              ([&]() {
+                // Need both `Bases` due to a language/compiler quirk
+                static_cast<const Bases&>(self).Bases::retrieve_code(query);
+                return query.has_value_;
+              }()) ||
+              ...);
+        }(BaseList{});
+
+    // Special case: UUID requests a formatter function. This branch is last to
+    // minimize the impact on the "fast path" of code retrieval.
+    //
+    // NB: The only way to avoid this extra branch on miss would be to force
+    // all errors to implement both `retrieve_code` and another piece of
+    // auto-synthesized boilerplate as below, and that didn't seem worth it.
+    //   void format_codes(
+    //       fmt::format_context&, const char* pre_separator) const override;
+    if (query.uuid_matches(rich_error_code_query::kFormatterUuid)) {
+      query.formatter_fn_ = format_all_codes_recursive;
+      query.has_value_ = true;
+      return;
+    }
+  }
+
+ private:
+  // Format all codes from this error and its bases
+  static bool format_all_codes_recursive(
+      const rich_error_base& err,
+      fmt::appender& out,
+      const char* pre_separator) {
+    bool has_code = false;
+    const Self& self = static_cast<const Self&>(err);
+    // Format own codes
+    (
+        [&]<auto CodeFn>(vtag_t<CodeFn>) {
+          has_code = true;
+          fmt::format_to(out, "{}", pre_separator);
+          pre_separator = ", ";
+          auto code = (self.*CodeFn)();
+          using Code = decltype(code);
+          const char* name = []() {
+            if constexpr (requires { rich_error_code<Code>::name; }) {
+              return rich_error_code<Code>::name;
+            } else {
+              return pretty_name<Code>();
+            }
+          }();
+
+          // Use `format_as_t` wrapper if provided, otherwise format directly.
+          // This lets `rich_error_code<Code>` provide custom formatting without
+          // specializing `fmt::formatter<Code>` (useful for standard types
+          // where such a specialization could cause ODR issues).
+          if constexpr (requires {
+                          typename rich_error_code<Code>::format_as_t;
+                        }) {
+            using FormatAs = typename rich_error_code<Code>::format_as_t;
+            static_assert(
+                fmt::is_formattable<FormatAs>::value,
+                "rich_error_code<Code>::format_as_t must be formattable");
+            fmt::format_to(out, "{}={}", name, FormatAs{code});
+          } else if constexpr (fmt::is_formattable<Code>::value) {
+            fmt::format_to(out, "{}={}", name, code);
+          } else {
+            // While most error codes should be formattable, and display a real
+            // message, it seems ok to allow lightweight enum-only codes where
+            // code brevity is key. (`fmt` doesn't format scoped enums)
+            static_assert(
+                std::is_enum_v<Code>,
+                "Code must be an enum or have a formatter");
+            fmt::format_to(
+                out,
+                "{}={}",
+                name,
+                static_cast<std::underlying_type_t<Code>>(code));
+          }
+        }(vtag<CodeFns>),
+        ...);
+
+    // Then, delegate to base classes to format their codes
+    [&]<typename... Bases>(tag_t<Bases...>) {
+      (
+          [&]() {
+            rich_error_code_query q{rich_error_code_query::kFormatterUuid};
+            static_cast<const Bases&>(self).Bases::retrieve_code(q);
+            if (q.has_value_) {
+              if (q.formatter_fn_(err, out, pre_separator)) {
+                has_code = true;
+              }
+              pre_separator = ", ";
+            }
+          }(),
+          ...);
+    }(BaseList{});
+    return has_code;
+  }
+};
+
+/// Extracts a typed error code from various error containers.
+///
+/// Returns a `Code` if:
+///   - The container holds an error derived from `rich_error_base`, AND
+///   - That error class supports `Code` (via `rich_error_bases_and_own_codes`)
+/// Otherwise, returns `std::nullopt`.
+///
+/// Supported containers:
+///   - Direct `rich_error_base` references
+///   - `std::exception_ptr` (via `exception_ptr_get_object_hint`)
+///   - `exception_wrapper`, `rich_exception_ptr`, `result`,
+///     `error_or_stopped`, once you include the respective header.
+///
+/// Example:
+///   if (auto code = get_rich_error_code<MyErrorCode>(exception_ptr)) {
+///     handle(*code);
+///   }
+///
+/// See `rich_error_code` docblock for how to make your `Code` usable here.
+///
+/// NOTE: This only queries for the exact `Code` type supplied, and does not
+/// look for any conversion paths from the codes supported by the underlying
+/// error to the `Code`.  This is OK and the right default behavior (esp since
+/// it's uncommon and frankly weird to design code hierarchies that are
+/// implicitly interconvertible).  However, if a compelling use-case arose,
+/// then you could introduce a separate "converting" query API, and export the
+/// available conversions via the `rich_error_code<>` trait:
+///   optional<uintptr_t> rich_error_code<Code>::convert_from_uuid(...);
+template <typename Code>
+class get_rich_error_code_fn {
+ public:
+  template <typename T>
+  [[nodiscard]] constexpr std::optional<Code> operator()(const T& t) const {
+    // Per `retrieve_code`, `mangled_code_` is uninitialized for reserved UUIDs!
+    //
+    // This was a `requires` clause, but some GCC versions have a bug where
+    // constraint normalization differs between friend declarations and class
+    // definitions, causing "redeclaration with different constraints" errors.
+    static_assert(
+        rich_error_code<Code>::uuid > rich_error_code_query::kMaxReservedUuid);
+    rich_error_code_query query{rich_error_code<Code>::uuid};
+    detail::get_rich_error_code_traits<T, void>::retrieve_code(t, query);
+    if (query.has_value_) {
+      return detail::unmangle_rich_error_code<Code>(query.mangled_code_);
+    }
+    return std::nullopt;
+  }
+};
+template <typename Code>
+inline constexpr get_rich_error_code_fn<Code> get_rich_error_code{};
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/rich_error_fwd.h b/folly/folly/result/rich_error_fwd.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_error_fwd.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+class rich_error_base;
+
+template <typename>
+class rich_error;
+
+class rich_exception_ptr;
+
+// DO NOT add random details here, see `result/detail/rich_error_common.h` e.g.
+namespace detail {
+// `std::exception` isn't constexpr in C++20, so there's no constexpr
+// `rich_error<T>`, but only `immortal_rich_error_storage<T>`.
+//
+// This passkey protects the ctor used to create leaky singletons of
+// `rich_error<T>` at runtime, for `get_exception` queries that require them.
+// As a result, immortal errors act as-if they **are** `std::exception`s.
+struct immortal_rich_error_private_t {};
+} // namespace detail
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/rich_exception_ptr.cpp b/folly/folly/result/rich_exception_ptr.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_exception_ptr.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/result/rich_exception_ptr.h>
+
+#include <ostream>
+
+#include <folly/Demangle.h>
+#include <folly/lang/Switch.h>
+#include <folly/result/rich_error_base.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+void rich_exception_ptr::format_to(
+    fmt::appender out, detail::format_to_skip_rich_t opts) const {
+  using B = detail::rich_exception_ptr_base;
+  using S = detail::private_rich_exception_ptr_sigil;
+  FOLLY_NON_EXHAUSTIVE_SWITCH(switch (get_bits()) {
+    case B::SIGIL_eq: {
+      auto sigil = static_cast<S>(get_uintptr());
+      FOLLY_EXHAUSTIVE_SWITCH({
+        switch (sigil) {
+          case S::RESULT_HAS_VALUE:
+            fmt::format_to(out, "[result has value]");
+            return;
+          case S::EMPTY_TRY:
+            fmt::format_to(out, "[empty Try]");
+            return;
+          default:
+            fmt::format_to(out, "[unknown sigil]");
+            return;
+        }
+      });
+    }
+
+    case B::NOTHROW_OPERATION_CANCELLED_eq:
+      fmt::format_to(out, "folly::detail::StoppedNoThrow");
+      return;
+
+      /* Future:
+    case B::SMALL_VALUE_eq:
+      fmt::format_to(out, "[small value]");
+      return;
+      */
+
+    case B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq:
+      if (!get_immortal_storage()) {
+        fmt::format_to(out, "[empty]");
+        return;
+      }
+      break;
+
+    default:
+      break;
+  });
+
+  // Rich error (immortal or owned)
+  if (!opts.skip_) {
+    if (auto* rex = get_outer_exception<rich_error_base>()) {
+      rex->format_with_epitaphs(out);
+      return;
+    }
+  }
+
+  // Owned non-rich exception
+  auto* type = exception_ptr_get_type(get_eptr_ref_guard().ref());
+
+  // Regular exception: demangle + what()
+  const char* type_name = type->name();
+  decltype(folly::demangle(type_name)) demangled;
+  // Demangling requires* an allocation, but we really don't want formatting
+  // to throw since it may make debugging OOMs painful.
+  //
+  // *If you need async-signal-safety, then try-allocate-catch is no good.
+  // `folly::demangle` also has a fixed-buffer version, for which you could
+  // allocate a few dozen bytes on stack here.
+  try {
+    demangled = folly::demangle(type_name);
+    type_name = demangled.c_str();
+  } catch (...) {
+  }
+  if (auto* ex = get_outer_exception<std::exception>()) {
+    fmt::format_to(out, "{}: {}", type_name, ex->what());
+  } else {
+    fmt::format_to(out, "{}", type_name);
+  }
+}
+
+std::ostream& operator<<(std::ostream& os, const rich_exception_ptr& rep) {
+  return detail::ostream_write_via_fmt(os, rep);
+}
+
+namespace detail {
+
+const std::exception_ptr& bad_result_access_singleton() {
+  // Union trick: the empty destructor ensures `value` is never destroyed.
+  // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
+  union Storage {
+    std::exception_ptr value;
+    Storage()
+        : value{make_exception_ptr_with(
+              std::in_place_type<bad_result_access_error>)} {}
+    ~Storage() {}
+  };
+
+  // Meyer singleton is thread-safe past C++11. Leak it to avoid SDOF.
+  static Storage storage;
+  return storage.value;
+}
+
+} // namespace detail
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/rich_exception_ptr.h b/folly/folly/result/rich_exception_ptr.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_exception_ptr.h
@@ -0,0 +1,1214 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h> // FOLLY_HAS_RESULT
+#include <folly/lang/Exception.h>
+#include <folly/result/detail/rich_exception_ptr_storage.h>
+#include <folly/result/rich_error_base.h>
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+struct OperationCancelled;
+
+template <typename, typename, auto...>
+class immortal_rich_error_t;
+
+namespace detail {
+
+// These are in `detail` since they're opt-build fallbacks for a debug-fatal.
+struct bad_result_access_error : public std::exception {};
+struct empty_result_error : public std::exception {};
+
+// Stub types that will be replaced by integrations with other folly/ types.
+//
+// Will be replaced by real types from OperationCancelled.h, which will both
+// derive from `OperationCancelled`.
+struct StoppedNoThrow {}; // NOT an `std::exception`
+struct StoppedMayThrow : std::exception {
+  const char* what() const noexcept override {
+    return "operation stopped (cancelled)";
+  }
+};
+// Will be replaced by the real types that are currently in Try.h
+struct StubUsingUninitializedTry : std::exception {};
+struct StubTryException : std::exception {};
+
+// Free function to get a singleton exception_ptr for bad_result_access
+const std::exception_ptr& bad_result_access_singleton();
+
+template <typename T>
+using detect_folly_detail_base_of_rich_error =
+    typename T::folly_detail_base_of_rich_error;
+
+// Tag types for non-public `rich_exception_ptr` interfaces
+struct force_slow_rtti_t {};
+struct try_rich_exception_ptr_private_t {};
+
+// Passkey for `rich_exception_ptr::format_to` optimization.
+// Lets `rich_error_base.cpp` skip a redundant maybe-RTTI "is rich?" check.
+class format_to_skip_rich_t {
+  friend class ::folly::rich_error_base;
+  friend class ::folly::rich_exception_ptr;
+  bool skip_;
+  explicit format_to_skip_rich_t(bool skip) : skip_(skip) {}
+
+ public:
+  format_to_skip_rich_t() : skip_(false) {}
+};
+
+template <typename Derived, typename B>
+// `protected` inheritance keeps the members of the storage classes as
+// implementation details, while allowing the derived `rich_exception_ptr`
+// (which is `final`) to access them for out-of-line member functions.
+class rich_exception_ptr_impl : protected B {
+ protected:
+  // Make unused code dead, while verifying that small-value code paths compile.
+  static constexpr bool kImplementsSmallValue = false;
+
+  constexpr const detail::immortal_exception_storage* get_immortal_storage()
+      const {
+    if (!std::is_constant_evaluated()) {
+      B::debug_assert("get_immortal_storage", !B::get_bits());
+    }
+    return B::get_immortal_storage_or_punned_uintptr();
+  }
+
+ private:
+  // This allows comparing REPs with different storage.  Only tests need it,
+  // since they explicitly cover both "separate" and "packed" storage, whereas
+  // `underlying_ptr()` always uses the default storage.
+  template <typename, typename>
+  friend class rich_exception_ptr_impl;
+
+  using typename B::bits_t;
+
+  constexpr void set_immortal_storage_and_bits(
+      const detail::immortal_exception_storage* p, typename B::bits_t bits) {
+    B::apply_bits_after_setting_data_with(
+        [p](auto& d) { d.immortal_storage_ = p; }, bits);
+  }
+
+  // Future: Could implement this overload via `folly::copy()`, if you find
+  // that `extract_exception_ptr` always optimizes away.
+  void assign_owned_eptr_and_bits(
+      const std::exception_ptr& ep, typename B::bits_t bits) {
+    B::apply_bits_after_setting_data_with(
+        [&ep](auto& d) {
+          new (&d.eptr_ref_guard_.ref()) std::exception_ptr{ep};
+        },
+        bits);
+  }
+
+  void assign_owned_eptr_and_bits(
+      std::exception_ptr&& ep, typename B::bits_t bits) {
+    B::apply_bits_after_setting_data_with(
+        [&ep](auto& d) {
+          new (&d.eptr_ref_guard_.ref())
+              std::exception_ptr{detail::extract_exception_ptr(std::move(ep))};
+        },
+        bits);
+  }
+
+  template <typename Ex>
+  consteval static void assert_operation_cancelled_queries() {
+    static_assert(
+        !std::is_same_v<const Ex, const StoppedNoThrow> &&
+            !std::is_same_v<const Ex, const StoppedMayThrow>,
+        "User code may only test for `OperationCancelled`; its derived classes "
+        "are private implementation details.");
+  }
+
+  template <typename MaybeConstEx>
+  MaybeConstEx* get_exception_from_nothrow_oc() const {
+    B::debug_assert(
+        "get_exception_from_nothrow_oc",
+        B::NOTHROW_OPERATION_CANCELLED_eq == B::get_bits());
+    const auto& epg = B::get_eptr_ref_guard();
+    // Since `assert_operation_cancelled_queries` forbids testing for
+    // the private derived classes of OC, we don't need `derived_from`.
+    if constexpr (std::
+                      is_same_v<const MaybeConstEx, const OperationCancelled>) {
+      return static_cast<MaybeConstEx*>(exception_ptr_get_object(epg.ref()));
+    }
+    return nullptr; // Stored OC, but queried for other type.
+  }
+
+  template <typename MaybeConstEx>
+  MaybeConstEx* get_exception_from_owned_eptr() const {
+    B::debug_assert(
+        "get_exception_from_owned_eptr",
+        B::OWNS_EXCEPTION_PTR_and & B::get_bits());
+    const auto& epg = B::get_eptr_ref_guard();
+    const auto& ep = epg.ref();
+    const auto fast_path_bits = B::mask_FAST_PATH_TYPES & B::get_bits();
+    // Fast path for `result::has_stopped()`.  This is exhaustive since
+    // `OperationCancelled` is the base for the only "official" cancellation
+    // signals -- user derived classes are forbidden via
+    // `assert_operation_cancelled_queries()`.
+    if constexpr (std::
+                      is_same_v<const MaybeConstEx, const OperationCancelled>) {
+      if (fast_path_bits == B::IS_OPERATION_CANCELLED_masked_eq) {
+        return static_cast<MaybeConstEx*>(exception_ptr_get_object(ep));
+      } else if (fast_path_bits != B::owned_eptr_UNKNOWN_TYPE_masked_eq) {
+        return nullptr; // Known type, but not `MaybeConstEx`
+      }
+      // ... fall through
+    } else if constexpr (
+        std::is_same_v<const MaybeConstEx, const rich_error_base>) {
+      // Fast path for `rich_error_base`.  We currently don't try to accelerate
+      // lookup of its derived classes, except for the "miss" case below.
+      //
+      // Future: See if knowing that the pointed-to type derives from
+      // `rich_error_base` gives a faster `dynamic_cast` than our fallback.
+      if (fast_path_bits == B::IS_RICH_ERROR_BASE_masked_eq) {
+        // The owned eptr ctor checks `rich_error_base` has offset 0
+        return static_cast<MaybeConstEx*>(exception_ptr_get_object(ep));
+      } else if (fast_path_bits != B::owned_eptr_UNKNOWN_TYPE_masked_eq) {
+        return nullptr; // Known type, but not `MaybeConstEx`
+      }
+      // ... fall through
+    }
+    if constexpr (std::derived_from<MaybeConstEx, rich_error_base>) {
+      if (fast_path_bits != B::IS_RICH_ERROR_BASE_masked_eq &&
+          fast_path_bits != B::owned_eptr_UNKNOWN_TYPE_masked_eq) {
+        return nullptr;
+      }
+    }
+    return exception_ptr_get_object_hint<MaybeConstEx>(ep);
+  }
+
+  static constexpr auto with_underlying_impl(auto* me, auto fn)
+      -> decltype(fn(me));
+
+  // Calls `fn` with the underlying exception, bypassing any stack of epitaph
+  // wrappers that `this` might represent, and returns the result.
+  //
+  // Note that `fn` must be generic, ready to accept either of:
+  //   - The `rich_exception_ptr_impl<...>` sliced from `rich_exception_ptr`
+  //     (default params!) returned by `rich_error_base::underlying_error()`, or
+  //   - ... this `rich_exception_ptr_impl*`, if that's a different type.
+  constexpr decltype(auto) with_underlying(auto fn) const {
+    return with_underlying_impl(this, fn);
+  }
+  constexpr decltype(auto) with_underlying(auto fn) {
+    return with_underlying_impl(this, fn);
+  }
+
+  // Pre-condition: `rich_exception_ptr` is in any valid state.
+  //
+  // Post-condition: Any stored owned object (i.e. eptr) is destructed, and
+  // all member vars of the object are in an undefined state.  To reuse it,
+  // they must be re-set as-if constructing it from scratch.
+  constexpr void destroy_owned_state_and_bits_caller_must_reset() {
+    if (B::OWNS_EXCEPTION_PTR_and & B::get_bits()) {
+      B::get_eptr_ref_guard().ref().~exception_ptr();
+    }
+  }
+
+  // IMPORTANT: Must not read `this`, it may be after-dtor, or before-ctor
+  constexpr void copy_unowned_pointer_sized_state(
+      const rich_exception_ptr_impl& other) {
+    if (!std::is_constant_evaluated()) {
+      // NB: Technically, this works for !B::bits_store_eptr(B::get_bits())),
+      // but we expect to use it in a subset of those cases.
+      B::debug_assert(
+          "copy_unowned_pointer_sized_state",
+          B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == other.get_bits() ||
+              B::SIGIL_eq == other.get_bits() ||
+              // We use this code path to copy / move small values uintptrs,
+              // the copy/move ctors must be called by whatever contains this.
+              B::SMALL_VALUE_eq == other.get_bits());
+    }
+    // Here, we might have an immortal error, or a sigil, a small value, etc.
+    // All of these require copying "bits + uintptr".  One way to achieve this:
+    //   set_immortal_storage_and_bits(
+    //       other.get_immortal_storage_or_punned_uintptr(), other.get_bits());
+    // However, that debug-asserts the first arg has clean low bits, which
+    // would only be true for immortals, and not for the other cases.  Rather
+    // than conditionally relax the assertion, or add other branches, let's
+    // just copy all bits of the storage state.  This is a bit wasteful on
+    // Windows, where eptr is 2 pointers, but it's the simplest code for now.
+    //
+    // Copy bit state storage, bypassing `rich_exception_ptr_impl::operator=`
+    B::operator=(other);
+  }
+
+  // IMPORTANT: Must not read `this`, it may be after-dtor, or before-ctor
+  constexpr void copy_from_impl(const rich_exception_ptr_impl& other) {
+    if (B::OWNS_EXCEPTION_PTR_and & other.get_bits()) {
+      assign_owned_eptr_and_bits(
+          other.get_eptr_ref_guard().ref(), other.get_bits());
+    } else if (B::NOTHROW_OPERATION_CANCELLED_eq == other.get_bits()) {
+      // Copy bit state storage, bypassing `rich_exception_ptr_impl::operator=`.
+      // DO NOT run the `std::exception_ptr` copy ctor; immortal OC is unowned.
+      B::operator=(other);
+    } else {
+      // Immortal RE; empty eptr; sigil uintptr; small value uintptr
+      copy_unowned_pointer_sized_state(other);
+    }
+  }
+
+  constexpr void make_moved_out(bits_t bits) {
+    // When `rich_exception_ptr` has an exception, the right moved-out state is
+    // "empty eptr".
+    if ((B::OWNS_EXCEPTION_PTR_and & bits) ||
+        B::NOTHROW_OPERATION_CANCELLED_eq == bits ||
+        B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == bits) {
+      set_immortal_storage_and_bits(
+          nullptr, B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq);
+    }
+    // Else: `this` is unchanged, since these pre-move states match post-move
+    // states: empty eptr; sigil uintptr; small value uintptr.
+  }
+
+  // IMPORTANT: Must not read `this`, it may be after-dtor, or before-ctor
+  constexpr void move_from_impl(rich_exception_ptr_impl&& other) noexcept {
+    auto other_bits = other.get_bits();
+    if (B::OWNS_EXCEPTION_PTR_and & other_bits) {
+      assign_owned_eptr_and_bits(
+          std::move(other.get_eptr_ref_guard().ref()), other_bits);
+    } else if (B::NOTHROW_OPERATION_CANCELLED_eq == other_bits) {
+      // Copy bit state storage, bypassing `rich_exception_ptr_impl::operator=`.
+      // DO NOT run the `std::exception_ptr` move ctor; immortal OC is unowned.
+      B::operator=(other);
+    } else {
+      // Immortal RE, empty eptr, sigil uintptr, small value uintptr
+      copy_unowned_pointer_sized_state(other);
+    }
+    other.make_moved_out(other_bits);
+  }
+
+  // Must call via `with_underlying()` -- code that rethrows may target `catch`,
+  // so the only right behavior is to drop epitaph wrappers.
+  template <bool PartOfTryImpl> //  `true` only when called from `Try.h`.
+  [[noreturn]] void throw_exception_impl() const {
+    bits_t bits = B::get_bits();
+    if (bits_t::OWNS_EXCEPTION_PTR_and & bits) {
+      const auto& epg = B::get_eptr_ref_guard();
+      const auto& ep = epg.ref();
+      if (ep) {
+        std::rethrow_exception(ep);
+      }
+      // Empty eptr, fall through to terminate...  Should not be reached --
+      // `from_exception_ptr_slow` rewrites empty eptrs as immortal `nullptr`s.
+      B::debug_assert("Bug: empty owned eptr stored inside a REP", false);
+    } else if (B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == bits) {
+      if (auto* immortal_storage = get_immortal_storage()) {
+        immortal_storage->throw_exception();
+        compiler_may_unsafely_assume_unreachable(); // required for [[noreturn]]
+      }
+      // Empty eptr, fall through to terminate...
+    } else if (bits_t::NOTHROW_OPERATION_CANCELLED_eq == bits) {
+      // Don't bother with the stored eptr since re-throwing does not
+      // preserve object identity.
+      // NOLINTNEXTLINE(facebook-hte-ThrowNonStdExceptionIssue)
+      throw StoppedNoThrow{};
+    } else if (B::SIGIL_eq == bits) {
+      // All sigil states represent non-error conditions (empty Try, has value).
+      // Calling throw_exception on these is a bug.
+      if (PartOfTryImpl) {
+        B::debug_assert(
+            "Try has unexpected sigil (not EMPTY_TRY)",
+            has_sigil<private_rich_exception_ptr_sigil::EMPTY_TRY>());
+        throw StubUsingUninitializedTry{};
+      } else {
+        B::debug_assert("Cannot `throw_exception` in sigil state", false);
+        throw bad_result_access_error{};
+      }
+    } else if (kImplementsSmallValue && B::SMALL_VALUE_eq == bits) {
+      if constexpr (PartOfTryImpl) {
+        throw StubTryException{}; // Match `Try::exception()` behavior
+      } else { // Match `result::error_or_stopped()` behavior
+        B::debug_assert("Cannot `throw_exception` in value state", false);
+        throw bad_result_access_error{};
+      }
+    }
+    // Else: the above bit tests are intended to exhaustively cover all allowed
+    // states, so we only fall through to terminate when:
+    //   - Our bit state is invalid.
+    //   - `this` contains an empty eptr.  Per the standard, rethrowing one is
+    //     UB, and our current termination behavior matches `exception_wrapper`.
+    // Future: maybe relax these to debug-fatal/opt-throw, like `result` does.
+    B::terminate_on_empty_or_invalid_eptr(bits);
+  }
+
+  // Call ONLY after handling owned-eptr in `to_exception_ptr_...()`
+  template <bool PartOfTryImpl> //  `true` only when called from `Try.h`.
+  std::exception_ptr to_exception_ptr_non_owned(bits_t bits) const {
+    if (B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == bits) {
+      const auto* s = get_immortal_storage();
+      return s ? s->to_exception_ptr() : std::exception_ptr{};
+    }
+    if constexpr (PartOfTryImpl) {
+      if (B::SIGIL_eq == bits) {
+        B::debug_assert(
+            "Try has unexpected sigil (not EMPTY_TRY)",
+            has_sigil<private_rich_exception_ptr_sigil::EMPTY_TRY>());
+        throw StubUsingUninitializedTry{};
+      } else if (kImplementsSmallValue && B::SMALL_VALUE_eq == bits) {
+        throw StubTryException{}; // Match `Try::exception()` behavior
+      } // Else: fall through...
+    }
+    // Sigil & small value match `result::error_or_stopped()` failure behavior
+    B::debug_assert(
+        "Cannot use `to_exception_ptr_slow` in sigil or small-value state",
+        false);
+    return bad_result_access_singleton();
+  }
+
+  template <bool PartOfTryImpl> //  `true` only when called from `Try.h`.
+  std::exception_ptr to_exception_ptr_copy() const {
+    // Code that examines `exception_ptr` may rethrow or use legacy APIs, so we
+    // have to drop epitaph wrappers.
+    return with_underlying([](auto* rep) {
+      auto bits = rep->get_bits();
+      if ((B::OWNS_EXCEPTION_PTR_and & bits) ||
+          // Future: It's quite silly that this copy op, and its eventual dtor,
+          // both contend on the atomic refcount for the singleton...  when a
+          // leaky singleton doesn't even need refcounts.  See the
+          // `get_outer_exception` doc for a bithacking idea to fix this.
+          (B::NOTHROW_OPERATION_CANCELLED_eq == bits)) {
+        return rep->get_eptr_ref_guard().ref();
+      }
+      return rep->template to_exception_ptr_non_owned<PartOfTryImpl>(bits);
+    });
+  }
+
+  template <bool PartOfTryImpl> //  `true` only when called from `Try.h`.
+  std::exception_ptr to_exception_ptr_move() {
+    // Similar logic to `to_exception_ptr_copy()`, but we are moving out the
+    // inner exception -- so that needs to be "moved out".  Then, any outer
+    // exceptions need to be destroyed.
+    std::exception_ptr eptr;
+    bits_t outer_bits = B::get_bits();
+    using bits_t = typename B::bits_t; // MSVC thinks `B::BIT_NAME` is private
+    bool this_is_wrapper = with_underlying([&eptr](auto* rep) {
+      bits_t bits = rep->get_bits();
+      // `with_underlying` makes `rep` a pointer-to-`const` iff `this` is not
+      // an underlying error, or equivalently if `rep` is not an outer error.
+      constexpr bool rep_is_not_outer =
+          std::is_const_v<std::remove_pointer_t<decltype(rep)>>;
+      if (bits_t::OWNS_EXCEPTION_PTR_and & bits) {
+        if constexpr (rep_is_not_outer) {
+          // When `rep` is not `this`, it comes from `with_underlying`, which
+          // (by design!) points to `const`.  So, the `else` branch shouldn't
+          // compile (again, by design), but more importantly, it would violate
+          // the principle that epitaph wrappers are transparent.  The doc
+          // of `rich_error_base::mutable_underlying_error` speaks to this.
+          // Concretely:
+          //   rich_exception_ptr rep1 = /* epitaph-wrapped MyErr */;
+          //   auto rep2 = rep1; // Both share the same `epitaph_non_value`!
+          // This would mutate `epitaph_non_value::next_` for *both*.
+          //   auto eptr2 = std::move(rep2).to_exception_ptr_slow();
+          // So `rep1` would now be a wrapper around a moved-out (empty) REP.
+          eptr = rep->get_eptr_ref_guard().ref();
+        } else { // If `rep` is `this`, then we can safely move it.
+          eptr = detail::extract_exception_ptr(
+              std::move(rep->get_eptr_ref_guard().ref()));
+        }
+      } else if (bits_t::NOTHROW_OPERATION_CANCELLED_eq == bits) {
+        // For nothrow OC, the stored eptr is a non-owning copy of a leaky
+        // singleton. We must copy (not move/extract) to properly increment
+        // refcounts, matching the behavior of `to_exception_ptr_copy()`.
+        //
+        // Future: See `to_exception_ptr_copy` for an optimization idea.
+        eptr = rep->get_eptr_ref_guard().ref();
+      } else {
+        eptr = rep->template to_exception_ptr_non_owned<PartOfTryImpl>(bits);
+      }
+      return rep_is_not_outer;
+    });
+    if (this_is_wrapper) {
+      // We just moved out the underlying error, but the wrapper chain still
+      // needs to be destroyed, or `make_moved_out` would leak it.
+      destroy_owned_state_and_bits_caller_must_reset();
+    }
+    make_moved_out(outer_bits);
+    return detail::extract_exception_ptr(std::move(eptr));
+  }
+
+  template <typename D1, typename B1, typename D2, typename B2>
+  static constexpr bool immortal_compares_equal(
+      // `B1` & `B2` may differ, see `operator==` and `compare_equal`
+      const rich_exception_ptr_impl<D1, B1>* immortal,
+      const rich_exception_ptr_impl<D2, B2>* other,
+      bits_t other_bits) {
+    if (B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == other_bits) {
+      // Why aren't we comparing `get_immortal_storage()->immortal_ptr_`?
+      //  (1) We only expect immortal `rich_exception_ptr`s to be created via
+      //      `immortal_rich_error`, so it is fine to assume that each immortal
+      //      exception has a unique `get_immortal_storage()`.
+      //  (2) `get_immortal_storage()` is null for `rich_exception_ptr` empty.
+      return immortal->get_immortal_storage() == other->get_immortal_storage();
+    } else if (B::OWNS_EXCEPTION_PTR_and & other_bits) {
+      if (auto* immortal_storage = immortal->get_immortal_storage()) {
+        // An immortal eptr can only compare equal to an owned one if the latter
+        // is a copy of the immortal's mutable singleton.  We don't want to
+        // eagerly create that singleton here, so instead check whether it has
+        // already been created.
+        //
+        // The right memory ordering here is TBD, but its impact is low, since
+        // it would only save a few cycles of "acquire" on ARM (acquire loads
+        // are free on x86).  For now, we go with (2), the more conservative of
+        // two conflicting points of view:
+        //
+        // (1) We may test for "is created" with a relaxed load of the
+        //     underlying `eptr_`, and only load the ref to the value when it
+        //     is known to be there.
+        //
+        // (2) Must use "acquire" ordering for happens-before synchronization
+        //     with the store-release in the singleton's `ensure_created()`.
+        //     On weaker memory models (e.g.  ARM), using "relaxed" here could
+        //     read a stale `nullptr` after `other` was copied from the same
+        //     singleton, causing us to incorrectly return `false`.
+        if (auto* mutable_eptr =
+                immortal_storage
+                    ->acquire_mutable_singleton_ptr_if_already_created()) {
+          return *mutable_eptr == other->get_eptr_ref_guard().ref();
+        }
+        return false;
+      } else { // `immortal` is empty
+        return !other->get_eptr_ref_guard().ref();
+      }
+    }
+    B::debug_assert(
+        "operator== / immortal exhaustiveness check",
+        // These states aren't errors
+        B::SMALL_VALUE_eq == other_bits || B::SIGIL_eq == other_bits ||
+            // Has eptr, but will never equal an immortal rich eptr, since
+            // nothrow OC uses its own separate singleton.
+            B::NOTHROW_OPERATION_CANCELLED_eq == other_bits);
+    return false;
+  }
+
+  // Must call via `with_underlying()` -- we want to compare the innermost
+  // exception, ignoring epitaph wrappers.
+  template <typename D1, typename B1, typename D2, typename B2>
+  static constexpr bool compare_equal(
+      const rich_exception_ptr_impl<D1, B1>* lp,
+      // The doc of `operator==` explains the argument asymmetry.
+      const rich_exception_ptr_impl<D2, B2>* rp) {
+    const auto lbits = lp->get_bits(), rbits = rp->get_bits();
+    if (B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == lbits) {
+      return immortal_compares_equal(lp, rp, rbits);
+    } else if (B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == rbits) {
+      // Future: Check if it's worth manually pruning the "immmortal
+      // other_bits" branch within this call (it was covered above).
+      return immortal_compares_equal(rp, lp, lbits);
+    } else if (
+        ((B::OWNS_EXCEPTION_PTR_and & lbits) ||
+         B::NOTHROW_OPERATION_CANCELLED_eq == lbits) &&
+        ((B::OWNS_EXCEPTION_PTR_and & rbits) ||
+         B::NOTHROW_OPERATION_CANCELLED_eq == rbits)) {
+      return lp->get_eptr_ref_guard().ref() == rp->get_eptr_ref_guard().ref();
+    } else if (
+        kImplementsSmallValue && B::SMALL_VALUE_eq == lbits &&
+        B::SMALL_VALUE_eq == rbits) {
+      B::debug_assert(
+          "rich_exception_ptr::operator== invalid for 2 small value uintptrs",
+          false);
+      // The fallback could trivally compare `get_uintptr()`, to return `true`
+      // correctly sometimes, but the the right comparison semantics must
+      // actually be determined by `result<T>` that knows the small-value type.
+      return false;
+    }
+    // Cases now ruled out:
+    //   - Either side is an immortal rich error.
+    //   - Both sides use eptr_ref_guard (either owned or nothrow OC).
+    //   - Both sides hold small value uintptrs.
+    //
+    // Those covered all comparisons that can go "across" bit states, namely
+    // comparing the various kinds of errors.  All the remaining heterogeneous
+    // comparisons are false:
+    //   small value uintptr VS eptr_ref_guard
+    //   sigil VS eptr_ref_guard
+    //   sigil VS small value uintptr
+    //
+    // This means that the only possible true comparison is "sigil VS sigil".
+    //
+    // NB: Here, we assume that we're part of the `Try` impl if the bits are
+    // `SIGIL_eq` -- this is done to avoid templating the container on "is it
+    // in `Try`?" to make it easier to interconvert `Try` and `result`.
+    if (B::SIGIL_eq == lbits && B::SIGIL_eq == rbits) {
+      // Both are sigils; compare the actual sigil values
+      return lp->get_uintptr() == rp->get_uintptr();
+    }
+    B::debug_assert("operator== exhaustiveness", lbits != rbits);
+    return false;
+  }
+
+ protected:
+  // Implementation for `rich_exception_ptr::from_exception_ptr_slow`.
+  //
+  // Future: This sets to "unknown type" to avoid doing RTTI eagerly.  In
+  // theory, our mutable getters could update `bits_` opportunistically.
+  rich_exception_ptr_impl(force_slow_rtti_t, std::exception_ptr&& e) {
+    assign_owned_eptr_and_bits(
+        std::move(e),
+        static_cast<bits_t>(
+            B::OWNS_EXCEPTION_PTR_and | B::owned_eptr_UNKNOWN_TYPE_masked_eq));
+  }
+
+  template <typename, typename, auto...>
+  friend class folly::immortal_rich_error_t;
+
+  // Used only by `immortal_rich_error`.  IMPORTANT: In any new use, address
+  // the pointer slicing risk flagged in the `IS_RICH_ERROR_BASE_...` docblock.
+  // `consteval` guarantees `p` is immortal.
+  template <typename Ex>
+  consteval rich_exception_ptr_impl(
+      const Ex* ex, const detail::immortal_exception_storage* p) {
+    set_immortal_storage_and_bits(p, B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq);
+    // Future: This constraint is to allow packing `rich_exception_ptr::bits_`
+    // into the unused bits of the pointer.  Once `constexpr` allows some
+    // pointer bit twiddling, we can trivially support constexpr
+    // `OperationCancelled`, as well as non-`rich_error_base` exception types.
+    static_assert( // Has manual test
+        std::derived_from<Ex, rich_error_base>,
+        "In C++20, `rich_exception_ptr` can only support immortal errors via "
+        "`immortal_rich_error`.");
+    // Redundant with `rich_error.h` asserts, so no manual test for this.
+    static_assert(detail::has_offset0_base<Ex, rich_error_base>);
+    if (ex->underlying_error()) {
+      // This simplifying assumption is good for perf -- this means that
+      // `with_underlying()` et al can test for "immortal rich error" and skip
+      // querying `underlying_error()` which would otherwise require an extra
+      // non-local load.  If this became needed, the load could be made local
+      // by denormalizing `underlying_error()` into `get_immortal_storage()`.
+      // NOLINTNEXTLINE(facebook-hte-ThrowNonStdExceptionIssue)
+      throw "For now, immortal errors don't support `underlying_error()`";
+    }
+  }
+
+ public:
+  constexpr ~rich_exception_ptr_impl() {
+    destroy_owned_state_and_bits_caller_must_reset();
+  }
+
+  /// The default-constructed state is like an empty `std::exception_ptr`
+  constexpr rich_exception_ptr_impl() {
+    set_immortal_storage_and_bits(
+        nullptr, B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq);
+  }
+
+  /// Construct in a sigil state. See `private_rich_exception_ptr_sigil`.
+  template <private_rich_exception_ptr_sigil S>
+  explicit rich_exception_ptr_impl(vtag_t<S>) {
+    B::apply_bits_after_setting_data_with(
+        [](auto& d) { d.uintptr_ = static_cast<uintptr_t>(S); }, B::SIGIL_eq);
+  }
+
+  /// Check for a specific sigil state. See `private_rich_exception_ptr_sigil`.
+  template <private_rich_exception_ptr_sigil S>
+  [[nodiscard]] constexpr bool has_sigil() const noexcept {
+    return B::SIGIL_eq == B::get_bits() &&
+        B::get_uintptr() == static_cast<uintptr_t>(S);
+  }
+
+  /// This constructor makes an "owning" `rich_exception_ptr`.
+  ///
+  /// Future: When needed, in-place construct from a callable, via either of:
+  ///   rich_exception_ptr rep{std::in_place_type<Ex>, ...}
+  ///   auto rep = rich_exception_ptr::make_with([](){ return Ex{...}; });
+  ///
+  /// On supported platforms, this is FAR more efficient than throw-and-catch.
+  /// Still, construction allocates, and copy/destroy perform atomic ops.
+  ///
+  /// For a cheap-to-make and cheap-to-copy "immortal" exception ptr, see
+  /// `immortal_rich_error.h`.
+  ///
+  /// IMPORTANT NOTE: We do NOT want users to construct `rich_exception_ptr`
+  /// from type-erased `std::exception_ptr` or `folly::exception_wrapper`,
+  /// because that would break RTTI-avoidance optimizations in `result` code.
+  template <std::derived_from<std::exception> Ex>
+  explicit rich_exception_ptr_impl(Ex ex) {
+    using bits_t = typename B::bits_t; // MSVC thinks `B::BIT_NAME` is private
+    constexpr bits_t bits{[]() -> bits_t {
+      if constexpr (std::derived_from<Ex, rich_error_base>) {
+        // Redundant with `rich_error.h` asserts, hence no manual test.
+        static_assert(detail::has_offset0_base<Ex, rich_error_base>);
+        return static_cast<bits_t>(
+            bits_t::OWNS_EXCEPTION_PTR_and |
+            bits_t::IS_RICH_ERROR_BASE_masked_eq);
+      } else if constexpr (std::derived_from<Ex, OperationCancelled>) {
+        // We only want the throwing version here, since nothrow OC uses a
+        // different ctor, and a non-owned copy of a leaky singleton.  This
+        // should never fire since `StoppedNoThrow` doesn't
+        // derive from `std::exception`, and `OperationCancelled` will soon
+        // no longer be directly constructible.
+        static_assert(
+            // FIXME: This one will go away:
+            std::is_same_v<const Ex, const OperationCancelled> ||
+            std::is_same_v<const Ex, const StoppedMayThrow>);
+        return static_cast<bits_t>(
+            bits_t::OWNS_EXCEPTION_PTR_and |
+            bits_t::IS_OPERATION_CANCELLED_masked_eq);
+      } else {
+        return static_cast<bits_t>(
+            bits_t::OWNS_EXCEPTION_PTR_and |
+            bits_t::owned_eptr_KNOWN_NON_FAST_PATH_TYPE_masked_eq);
+      }
+    }()};
+    assign_owned_eptr_and_bits(
+        make_exception_ptr_with(std::in_place, std::move(ex)), bits);
+  }
+
+  /// Copy constructor & assignment
+  constexpr rich_exception_ptr_impl(const rich_exception_ptr_impl& other) {
+    copy_from_impl(other);
+  }
+  rich_exception_ptr_impl& operator=(const rich_exception_ptr_impl& other) {
+    if (this != &other) {
+      destroy_owned_state_and_bits_caller_must_reset();
+      copy_from_impl(other);
+    }
+    return *this;
+  }
+
+  /// Move constructor & assignment
+  constexpr rich_exception_ptr_impl(rich_exception_ptr_impl&& other) noexcept {
+    move_from_impl(std::move(other));
+  }
+  rich_exception_ptr_impl& operator=(rich_exception_ptr_impl&& other) noexcept {
+    if (this != &other) {
+      destroy_owned_state_and_bits_caller_must_reset();
+      move_from_impl(std::move(other));
+    }
+    return *this;
+  }
+
+  // AVOID.  Prefer `rich_exception_ptr{YourErr{}}`, since knowing the type
+  // statically can avoid RTTI for `rich_error.h` types, and can speed up
+  // `result<T>::has_stopped()`.
+  //
+  // Pass by-&& because `std::exception_ptr` isn't always efficiently movable,
+  // and we have a workaround for this.
+  static Derived from_exception_ptr_slow(std::exception_ptr&& e) {
+    if (!e) {
+      return Derived{}; // "Owned" never stores an empty eptr.
+    }
+    return Derived{detail::force_slow_rtti_t{}, std::move(e)};
+  }
+
+  // PRIVATE, not for end users -- the non-stub type will be in `detail`.
+  // Users will instead use `co_yield co_stopped_nothrow` in coros.
+  explicit rich_exception_ptr_impl(StoppedNoThrow) {
+    // Wrapper that constructs the exception_ptr on first use.
+    // No destructor needed - mutable_eptr_ref_guard is POD-like storage,
+    // so the exception_ptr is leaked to avoid SDOF.
+    struct InitializedSingleton : B::mutable_eptr_ref_guard {
+      InitializedSingleton() {
+        new (&this->ref()) std::exception_ptr{
+            make_exception_ptr_with(std::in_place_type<StoppedNoThrow>)};
+      }
+    };
+    // Meyer singleton is thread-safe past C++11.
+    // Yes, this will make an instance per DSO. That's expected and OK.
+    // NOLINTNEXTLINE(facebook-hte-InlinedStaticLocalVariableWarning)
+    static InitializedSingleton singleton;
+    B::apply_bits_after_setting_data_with(
+        // Bitwise-copy the singleton without bumping the eptr's refcount
+        [&](auto& d) { d.eptr_ref_guard_ = singleton; },
+        B::NOTHROW_OPERATION_CANCELLED_eq);
+  }
+
+  /// Throws the innermost exception, ignoring epitaph wrappers.
+  ///
+  /// Precondition: Contains a nonempty exception.  Terminates on empty
+  /// `exception_ptr`, on invalid internal state.  Small-value is debug-fatal.
+  [[noreturn]] void throw_exception() const {
+    with_underlying([](auto* rep) {
+      rep->template throw_exception_impl</*PartOfTryImpl=*/false>();
+    });
+    compiler_may_unsafely_assume_unreachable(); // required for [[noreturn]]
+  }
+  // PRIVATE TO `Try`: `throw_exception()` with slight behavior differences.
+  [[noreturn]] void throw_exception(try_rich_exception_ptr_private_t) const {
+    with_underlying([](auto* rep) {
+      rep->template throw_exception_impl</*PartOfTryImpl=*/true>();
+    });
+    compiler_may_unsafely_assume_unreachable(); // required for [[noreturn]]
+  }
+
+  /// Returns the `std::exception_ptr` for the innermost exception, DISCARDING
+  /// epitaph wrappers.
+  ///
+  /// Overload differences:
+  ///   - `const&` copies the inner eptr (cost: an atomic refcount increment).
+  ///   - `&&` moves the inner eptr, destroys any epitaph wrappers, and
+  ///     leaves `this` in a moved-out, empty eptr state.
+  ///
+  /// Precondition: Contains an exception, or empty eptr (debug-fatal otherwise)
+  [[nodiscard]] std::exception_ptr to_exception_ptr_slow() const& {
+    return to_exception_ptr_copy</*PartOfTryImpl=*/false>();
+  }
+  [[nodiscard]] std::exception_ptr to_exception_ptr_slow() && {
+    return to_exception_ptr_move</*PartOfTryImpl=*/false>();
+  }
+  // PRIVATE TO `Try`: `to_exception_ptr_slow()` with edge case differences.
+  [[nodiscard]] std::exception_ptr to_exception_ptr_slow(
+      try_rich_exception_ptr_private_t) const& {
+    return to_exception_ptr_copy</*PartOfTryImpl=*/true>();
+  }
+  [[nodiscard]] std::exception_ptr to_exception_ptr_slow(
+      try_rich_exception_ptr_private_t) && {
+    return to_exception_ptr_move</*PartOfTryImpl=*/true>();
+  }
+
+  /// Returns `true` when both `lhs` and `rhs`...
+  ///  - ... point at the same underlying exception, per the details below.
+  ///  - ... occur in the `Try` implementation, and both contain empty `Try`
+  ///        (similarly for other non-user-observable sigils)
+  ///
+  /// When `lhs` and `rhs` are both representable as eptrs, we compare the
+  /// underlying exception object **pointers** -- ignoring epitaph wrappers.
+  ///
+  /// Caveat 1: If the same `immortal_rich_error<...>::ptr()` is instantiated
+  /// in multiple DSOs, then you may end up with multiple copies of the
+  /// underlying error, and their pointers will compare unequal.  To stop this,
+  /// pick a single link unit to instantiate your `immortal_rich_error`, and
+  /// expose only the `rich_exception_ptr` via the corresponding header.
+  ///
+  /// Caveat 2: Since `std::exception_ptr` isn't `constexpr`, an immortal rich
+  /// errors lazily makes an eptr singleton once needed.  Actually, there are
+  /// two singletons -- immutable & mutable -- for reasons discussed on
+  /// `get_outer_exception()`.  It is worth knowing that `operator==` can
+  /// compare an "owned eptr" with the mutable singleton of a "immortal rich
+  /// error".  This will happen, for example, if you copy
+  /// `to_exception_ptr_slow`:
+  ///
+  ///   auto rep1 = immortal_error<YourErr>::get();
+  ///   auto rep2 = rich_exception_ptr::from_exception_ptr_slow(
+  ///       rep.to_exception_ptr_slow());
+  ///   assert(rep1 == rep2);
+  ///
+  /// The immutable singleton is never exposed as an eptr, so the 2-singleton
+  /// inconsistency is not observable via this `operator==`.
+  template <typename D2, typename B2>
+  friend constexpr inline bool operator==(
+      const rich_exception_ptr_impl& lhs,
+      // The template setup is asymmetric, but the implementation should anyhow
+      // be symmetric in lhs & rhs, so I don't expect issues from that.
+      //
+      // Besides symmetry, the key implementation constraint is NEVER to
+      // use the union members or `bits_`.  Always, use the base-independent
+      // accessors -- this is required to support cross-base comparison.
+      //
+      // Note that this doesn't allow cross-value comparisons YET, just like
+      // `folly::result`, but this can be extended to follow `std::expected`.
+      const rich_exception_ptr_impl<D2, B2>& rhs) {
+    return lhs.with_underlying([&rhs](const auto* lp) {
+      return rhs.with_underlying([lp](const auto* rp) {
+        // MSVC needs full qualification here
+        return rich_exception_ptr_impl<Derived, B>::compare_equal(lp, rp);
+      });
+    });
+  }
+
+  // Like `folly::get_exception<Ex>()`, but accesses the actual outermost
+  // exception, even if it's wrapping another one -- instead of walking to the
+  // underlying error.
+  //
+  // CAREFUL: The type of this exception is different from whatever error
+  // actually occurred, see `epitaph.h` for the most common example.
+  //
+  // This is used by `rich_error_base::format_to` to walk the epitaph stack.
+  template <typename Ex>
+  constexpr Ex const* get_outer_exception() const noexcept
+      [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] {
+    assert_operation_cancelled_queries<Ex>();
+    bits_t bits = B::get_bits();
+    if (B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == bits) {
+      if (!get_immortal_storage()) {
+        return nullptr; // empty eptr
+      }
+
+      // The goal of the branches below is to hide from the end user that
+      // (until C++26) `rich_error<UserBase>` cannot be `constexpr`, and thus
+      // `immortal_ptr_` is `immortal_rich_error_storage<UserBase>`.
+      //
+      // So, the below tests for `folly_detail_base_of_rich_error` (2nd) and
+      // `std::exception` (1st) aren't just optimizations, we need them to
+      // correctly query `Ex` types that derive from `std::exception`.
+      //
+      // Internally, both `get_immortal_storage()->as_immutable_*` calls
+      // instantiate an immortal singleton of `rich_error<UserBase>` with a
+      // copy of the `constexpr` instance of `UserBase`.
+      if constexpr (std::is_same_v<const std::exception, const Ex>) {
+        return get_immortal_storage()->as_immutable_std_exception();
+      }
+
+      const rich_error_base* immortal_ptr =
+          get_immortal_storage()->immortal_ptr_;
+
+      // WATCH OUT: The `rich_error<Err>` part of this "fast path" logic is NOT
+      // best-effort, but mandatory, see the prior comment.
+      //
+      // The "exact type" tests below can never pass if `Ex` isn't related to
+      // `rich_error_base` (e.g.  `std::runtime_error`), and the `static_cast`
+      // won't even compile.
+      if constexpr (std::derived_from<Ex, rich_error_base>) {
+        // Querying for `rich_error<Err>`?
+        if constexpr (is_detected_v<
+                          detect_folly_detail_base_of_rich_error,
+                          Ex>) {
+          // Querying for `rich_error<Err>` when the immortal has `Err`.
+          if (&typeid(Ex) == get_immortal_storage()->rich_error_leaf_type_ ||
+              // We MUST fall back to the slower comparison of `type_info`
+              // objects, since in multi-DSO programs, there may exist multiple
+              // `type_info` pointers for the same type.
+              typeid(Ex) == *get_immortal_storage()->rich_error_leaf_type_) {
+            return static_cast<const Ex*>(
+                get_immortal_storage()->as_immutable_leaf_rich_error());
+          }
+          // Since we compared `type_info`s (and not just pointers), there's no
+          // need to fall back to `dynamic_cast` -- `rich_error<>` is final.
+          return nullptr;
+        } else { // Querying for non-leaf (abstract) `Ex`...
+          // Querying for `Ex` when the immortal stores `Ex`.
+          //
+          // Best-effort "fast path" to avoid RTTI when the query type exactly
+          // matches the stored type.  This optimization may fail in multi-DSO
+          // code (where many copies of the same `type_info` may exist at
+          // different addresses).  Like `exception_ptr_get_object_hint`, this
+          // gives a nice speedup for the common case.
+          //
+          // Only compare pointers, not `type_info` -- falling back to
+          // `dynamic_cast` will do that part anyway.
+          //
+          // Future: Neither this branch, nor the `get_mutable_exception` one
+          // uses `Ex::folly_get_exception_hint_types`, since the "exact type"
+          // checks take care of the common case.  Implementing hint support is
+          // possible, but first we need a use-case where it matters.
+          if (&typeid(Ex) == get_immortal_storage()->user_base_type_) {
+            return static_cast<const Ex*>(immortal_ptr);
+          }
+        }
+      }
+      // Fallback. Cheap & RTTI-free only when `Ex == rich_error_base`.
+      //
+      // Future: See `docs/future_fast_rtti.md` for how to avoid RTTI entirely.
+      //
+      // Future: If we do later support non-rich immortals, keep in mind that
+      // `dynamic_cast` only works when the pointed-to expression is
+      // polymorphic -- deriving from `std::exception` or `rich_error_base` is
+      // fine.  We may require one of these bases for immortals.
+      return dynamic_cast<const Ex*>(immortal_ptr);
+    } else if (B::OWNS_EXCEPTION_PTR_and & bits) {
+      return get_exception_from_owned_eptr<const Ex>();
+    } else if (B::NOTHROW_OPERATION_CANCELLED_eq == bits) {
+      return get_exception_from_nothrow_oc<const Ex>();
+    }
+    // The remaining bit states are not exceptions, so `nullptr` is a good
+    // match for `folly::get_exception(result<T>)` semantics.
+    B::debug_assert(
+        "const get_outer_exception unexpected bits",
+        B::SIGIL_eq == bits || B::SMALL_VALUE_eq == bits);
+    return nullptr;
+  }
+
+  /// See docs on the `const` overload of `get_outer_exception<Ex>()`.  The
+  /// mutable one mainly exists to implement `folly::get_mutable_exception<>()`.
+  ///
+  /// IMPORTANT: For immortal errors, there is no read-after-write consistency
+  /// in the sense that you might expect.  Rather, this returns a pointer into
+  /// a mutable, immortal singleton of `rich_error<UserBase>` that is
+  /// completely separate from the one that the `const` overload accesses.
+  ///
+  /// ## Why don't you have ONE read/write singleton for immortals?
+  ///
+  /// We could, but it would broadly add cost for a use-case that shouldn't
+  /// really come up.  If your edge-case usage relies on mutable access to an
+  /// immortal, you're probably aware of this gotcha, and can arrange to
+  /// call `get_mutable_exception` or the non-`const` overload of
+  /// `get_outer_exception` where it matters.
+  ///
+  /// Here's a closer look at the downsides of a 1-singleton implementation:
+  ///   - `get_exception<UserBase>()` & `get_exception<rich_error<UserBase>>()`
+  ///     must access the same instance.  To achieve this, immortals would need
+  ///     a mutable pointer to redirect from the constexpr `UserBase` to the
+  ///     immortal singleton, as soon as the first mutable access happens.
+  ///   - This pointer needs to be a relaxed read / relaxed write atomic,
+  ///     which, while not very costly, adds indirection.  This also implies a
+  ///     second indirection, for a virtual `immortal_ptr()` instead of the
+  ///     current `immortal_ptr_`.  Finally, there'd be an extra branch.  The
+  ///     expected extra cost is 3-5ns without memory contention, which is as
+  ///     much as the current `get_rich_error()` latency.
+  ///   - A further plus for the current 2-singletons implementation is that
+  ///     it’s technically possible to avoid the atomic-increment cost of
+  ///     `to_exception_ptr_slow()` through some bithacking.  Namely, the
+  ///     mutable singleton `std::exception_ptr` is initially prepared with a
+  ///     Very High Refcount of 2^63, and we reset it if it goes too low or too
+  ///     high.  A concrete pattern would involve a relaxed-atomic check of the
+  ///     first 2 bits.  If the bits are 00, or 11, then reset back to 2^63.
+  ///     This allows safely handing out copies without bumping the refcount --
+  ///     it would only break if a user simultaneously stored over 2^63 eptrs.
+  template <typename Ex>
+  constexpr Ex* get_outer_exception() noexcept
+      [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] {
+    assert_operation_cancelled_queries<Ex>();
+    // This mirrors the `const` overload, read that first for the comments.
+    bits_t bits = B::get_bits();
+    if (B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == bits) {
+      if (!get_immortal_storage()) {
+        return nullptr;
+      }
+      B::debug_assert(
+          "mutable get_outer_exception",
+          B::get_bits() == B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq);
+      if constexpr (std::is_same_v<const std::exception, const Ex>) {
+        return get_immortal_storage()->as_mutable_std_exception();
+      }
+      auto* leaf_rich_error =
+          get_immortal_storage()->as_mutable_leaf_rich_error();
+      if constexpr (std::derived_from<Ex, rich_error_base>) {
+        // Try to avoid RTTI when the immortal stores `Err` and the query is
+        // either `Err` or `rich_error<Err>`.
+        auto* type_to_match =
+            is_detected_v<detect_folly_detail_base_of_rich_error, Ex>
+            // NB: This could "miss" 8x faster by reusing the `const` pattern,
+            // but...  people really shouldn't use mutable errors, anyhow!
+            ? get_immortal_storage()->rich_error_leaf_type_
+            : get_immortal_storage()->user_base_type_;
+        // Best-effort optimization, may fail in multi-DSO code: Compare only
+        // ptrs, not `type_info` -- `dynamic_cast` will do that part anyway.
+        if (&typeid(Ex) == type_to_match) {
+          return static_cast<Ex*>(leaf_rich_error);
+        }
+      }
+      return dynamic_cast<Ex*>(leaf_rich_error);
+    } else if (B::OWNS_EXCEPTION_PTR_and & bits) {
+      return get_exception_from_owned_eptr<Ex>();
+    } else if (B::NOTHROW_OPERATION_CANCELLED_eq == bits) {
+      return get_exception_from_nothrow_oc<Ex>();
+    }
+    B::debug_assert(
+        "mutable get_outer_exception unexpected bits",
+        B::SIGIL_eq == bits || B::SMALL_VALUE_eq == bits);
+    return nullptr;
+  }
+
+ private:
+  /// Returns the `typeid` of the innermost exception object, ignoring
+  /// epitaph wrappers.  If no exception object is stored, returns null.
+  ///
+  /// UNFINISHED: Could perhaps be made public, currently PRIVATE due to the API
+  /// design issue with immortals documented inline.  That would simplify the
+  /// `rich_error_base` plumbing.
+  constexpr const std::type_info* UNFINISHED_exception_type() const noexcept {
+    using bits_t = typename B::bits_t; // MSVC thinks `B::BIT_NAME` is private
+    return with_underlying([](auto* rep) -> const std::type_info* {
+      if (bits_t::OWNS_EXCEPTION_PTR_and & rep->get_bits()) {
+        return exception_ptr_get_type(rep->get_eptr_ref_guard().ref());
+      } else if (
+          bits_t::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == rep->get_bits()) {
+        // This shouldn't even be hit on our internal formatting code path.
+        //
+        // FIXME: This is potentially too confusing for a public API, since:
+        //  - The actual storage type is `immortal_rich_error_storage`, which
+        //    is not user-addressable.
+        //  - The user can get `const UserBase*`, `rich_error<UserBase>*>, or
+        //    `const rich_error<UserBase>*`, but the latter 2 have costs,
+        //    and all 3 are distinct objects.
+        return rep->get_immortal_storage()
+            ? rep->get_immortal_storage()->user_base_type_
+            : nullptr;
+      } else if (bits_t::NOTHROW_OPERATION_CANCELLED_eq == rep->get_bits()) {
+        return &typeid(StoppedNoThrow);
+      }
+      return nullptr; // Non-exceptions: sigil uintptr, small value uintptr
+    });
+  }
+
+  // Calls `get_outer_exception` on the underlying error in the epitaph stack
+  template <typename CEx>
+  static constexpr rich_ptr_to_underlying_error<CEx> get_exception_impl(
+      auto* rep);
+
+ public:
+  /// Implementation of `folly::get_exception<Ex>(rich_exception_ptr)`
+  ///
+  /// Transparently handles errors with epitaph wrappers -- the returned
+  /// pointer-like resolves to the underlying, original exception. But, `fmt` or
+  /// `ostream::operator<<` will display the full epitaph stack.
+  ///
+  /// Avoid converting the result to `Ex*`, or you will lose the epitaphs.
+  ///
+  /// Sample usage:
+  ///
+  ///   if (auto ex = get_exception<Ex>(...)) { // NOT `Ex* ex`!
+  ///     LOG(INFO) << ex; // Will include epitaphs
+  ///   }
+  ///
+  /// IMPORTANT: For immortal errors, this `const` accessor will access a
+  /// different instance than `get_mutable_exception` does.  This will not
+  /// matter for 99% of usage, since mutating exceptions is VERY rare, and
+  /// likely a design smell.  In short, this was done is to avoid globally
+  /// imposing the cost of synchronization for the sake of a tiny minority
+  /// use-case.  The non-`const` overload of `get_outer_exception` says more.
+  ///
+  /// Future: There's no return state for "did not match `Ex` (aka `nullptr`),
+  /// but still have epitaphs" -- but it is easy to add if useful.
+  template <typename Ex>
+  constexpr rich_ptr_to_underlying_error<const Ex> get_exception(
+      get_exception_tag_t) const noexcept {
+    return get_exception_impl<const Ex>(this);
+  }
+  // IMPORTANT: For immortal errors, this will access a different instance than
+  // `get_exception` does, see the details in the non-`const` overload of
+  // `get_outer_exception`.
+  template <typename Ex>
+  constexpr rich_ptr_to_underlying_error<Ex> get_mutable_exception(
+      get_exception_tag_t) noexcept {
+    return get_exception_impl<Ex>(this);
+  }
+};
+
+using rich_exception_ptr_base = rich_exception_ptr_impl<
+    rich_exception_ptr,
+    conditional_t<
+        rich_exception_ptr_packed_storage::is_supported,
+        rich_exception_ptr_packed_storage,
+        rich_exception_ptr_separate_storage>>;
+
+} // namespace detail
+
+/// `rich_exception_ptr` is an analog of `exception_wrapper` or
+/// `std::exception_ptr`, with some extra efficiency optimizations, and
+/// integration with `rich_error` / `epitaph`.  It was designed to
+/// support rich-error features in `result.h`.
+///
+/// This class typically owns a `std::exception_ptr`, or stores a cheap-to-copy
+/// pointer to an immortal exception.  Unlike `exception_wrapper`, it enables
+/// RTTI-free lookup for `rich_error_base` & `result<T>::has_stopped()`.
+///
+/// However, private APIs can store some other states in its internal union.
+/// For this reason, `rich_exception_ptr` should not be used in most end-user
+/// code.  Instead, use `error_or_stopped`.
+///
+/// While generally aligned to `exception_wrapper`, this API is much smaller.
+/// Notably, the ONLY way to test `rich_exception_ptr` for the presence of the
+/// exception type `Ex` is via `folly::get_exception<Ex>(rich_eptr)`.
+class [[nodiscard]]
+rich_exception_ptr final : public detail::rich_exception_ptr_base {
+  using detail::rich_exception_ptr_base::rich_exception_ptr_base;
+
+ public:
+  void format_to(
+      fmt::appender out, detail::format_to_skip_rich_t opts = {}) const;
+};
+
+namespace detail {
+
+template <typename Derived, typename B>
+/*static*/ constexpr inline auto
+rich_exception_ptr_impl<Derived, B>::with_underlying_impl(auto* me, auto fn)
+    -> decltype(fn(me)) {
+  // Why only these bits, and no others?
+  //   - The `consteval rich_exception_ptr_impl` ctor guarantees that immortals
+  //     don't have a `underlying_error()` & explains how to extend if needed.
+  //   - None of the other states, nothrow OC included, are rich errors.
+  if (B::OWNS_EXCEPTION_PTR_and & me->get_bits()) {
+    if (const auto* rex = me->template get_outer_exception<rich_error_base>()) {
+      // The doc of `mutable_underlying_error()` explains why the `const` here
+      // is crucial.  Also, `to_exception_ptr_move` logic above relies on this.
+      if (const auto* rep = rex->underlying_error()) {
+        return fn(rep);
+      }
+    }
+  }
+  return fn(me);
+}
+
+// Specializes the `with_underlying()` traversal for `get_exception<>()`, while
+// populating `top_rich_error_` to support formatting with epitaphs.
+//
+// Future: A possible micro-optimization idea to try to save 1-2ns would be to
+// deduplicate the PLT call to `exception_ptr_get_object` (one per
+// `get_outer_exception` call below).
+template <typename Derived, typename B>
+template <typename CEx>
+constexpr inline auto rich_exception_ptr_impl<Derived, B>::get_exception_impl(
+    auto* rep) -> rich_ptr_to_underlying_error<CEx> {
+  assert_operation_cancelled_queries<CEx>();
+  const rich_error_base* top_rich_error = nullptr;
+  auto bits = rep->get_bits();
+  if (B::IS_IMMORTAL_RICH_ERROR_OR_EMPTY_eq == bits) {
+    // Per the `consteval rich_exception_ptr_impl` ctor, all immortals are
+    // underlying.  So, they get special logic to set `top_rich_error_`.
+    if constexpr (std::derived_from<CEx, rich_error_base>) {
+      auto* ex = rep->template get_outer_exception<CEx>();
+      return {ex, static_cast<const rich_error_base*>(ex)};
+    } else if constexpr (!std::is_same_v<const CEx, const std::exception>) {
+      // Immortals are rich, but query is neither std::exception, nor rich.
+      return {nullptr, nullptr};
+    } // else: `std::exception` IS accessible on immortals, needs 2 queries:
+    top_rich_error = rep->template get_outer_exception<rich_error_base>();
+    // Fall through to query for `CEx` in the final `return`...
+  } else if (B::OWNS_EXCEPTION_PTR_and & bits) {
+    // This dynamic eptr may be an epitaph wrapper, so we have to retrieve
+    // the underlying error.  This gives us `top_rich_error_` for free.
+    if (auto* rex = rep->template get_outer_exception<rich_error_base>()) {
+      top_rich_error = rex;
+      if (auto* new_rep = [rex]() {
+            if constexpr (std::is_const_v<CEx>) {
+              return rex->underlying_error();
+            } else {
+              return rex->mutable_underlying_error(
+                  rich_error_base::underlying_error_private_t{});
+            }
+          }()) {
+        // We found the underlying error, time to query `CEx`!
+        // NB: Falling through with `rep = new_rep;` emitted slower code.
+        return {new_rep->template get_outer_exception<CEx>(), top_rich_error};
+      } else if constexpr (std::is_same_v<const CEx, const rich_error_base>) {
+        // `rex` is underlying error, and exactly what was queried
+        return {rex, top_rich_error};
+      } // Else: `rep` is underlying & `top_rich_error` is set; fall through...
+    } else if constexpr (std::derived_from<CEx, rich_error_base>) {
+      // Outer isn't a `rich_error_base`, so it's also the underlying error,
+      // and thus cannot match any `rich_error_base`-derived `CEx`.
+      return {nullptr, nullptr};
+    } // Else: `rep` is underlying & non-rich; `top_rich_error` stays null...
+    // ... fall through to query `CEx`.
+  } // Else: We only need to resolve underlying for "owned" bits.
+  return {rep->template get_outer_exception<CEx>(), top_rich_error};
+}
+
+} // namespace detail
+
+std::ostream& operator<<(std::ostream&, const rich_exception_ptr&);
+
+} // namespace folly
+
+template <>
+struct fmt::formatter<folly::rich_exception_ptr> {
+  constexpr fmt::format_parse_context::iterator parse(
+      fmt::format_parse_context& ctx) {
+    return ctx.begin();
+  }
+  fmt::format_context::iterator format(
+      const folly::rich_exception_ptr& rep, fmt::format_context& ctx) const {
+    rep.format_to(ctx.out());
+    return ctx.out();
+  }
+};
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/rich_msg.h b/folly/folly/result/rich_msg.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/rich_msg.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fmt/format.h>
+
+#include <folly/Utility.h>
+#include <folly/lang/Exception.h>
+#include <folly/lang/SafeAssert.h>
+#include <folly/portability/SourceLocation.h>
+
+namespace folly {
+
+class rich_msg;
+
+namespace ext { // For extension authors -- public details
+
+/// ext::format_string_and_location
+///
+/// Rich error users probably want `rich_msg` instead, see below.  This is meant
+/// to enable library authors to make concise APIs, in the style
+/// `epitaph` or `...coded_rich_error`.
+///
+/// Captures a source location together with a literal string -- either for
+/// `fmt` formatting with arguments, or one without substitutions. Usage:
+/// template <typename... As> void
+/// yourFn(format_string_and_location<std::type_identity_t<As>...> = "")
+///
+/// The `type_identity` allows implicit construction of this type from string
+/// literals (its sole ctor is consteval).  Adding that nested template makes
+/// the argument non-deducible.  Without that, template deduction would attempt
+/// to deduce `const char [N]` -> `format_string_and_location`, which would fail
+/// since template deduction does not consider implicit conversions.
+///
+/// BUG ALERT: Due to https://github.com/llvm/llvm-project/issues/137907 avoid
+/// writing constructors like this: T(source_location sl =
+/// source_location::current()) A constructor with a defaulted
+/// `source_location` should take at least one mandatory argument to avoid this
+/// pitfall.
+template <typename... Args>
+class format_string_and_location {
+ private:
+  friend class ::folly::rich_msg;
+
+  source_location loc_;
+  fmt::format_string<Args...> fmt_str_;
+  literal_c_str lit_str_;
+
+ public:
+  /* implicit */ consteval format_string_and_location(
+      const char* str, source_location loc = source_location::current())
+      : loc_{std::move(loc)}, fmt_str_{str}, lit_str_{str} {}
+
+  constexpr exception_shared_string as_exception_shared_string(Args const&...)
+    requires(sizeof...(Args) == 0)
+  {
+    return exception_shared_string{lit_str_};
+  }
+
+  exception_shared_string as_exception_shared_string(Args const&... args)
+    requires(sizeof...(Args) > 0)
+  {
+    // `fmt::runtime` is safe since our ctor checked the string/args combo
+    //
+    // Future: If this code shows up in your benchmark, check out the test
+    // `exception_shared_string_format` for a possible micro-optimization that
+    // involves customizing the type given to `fmt` based on
+    // `is_register_pass_v`.
+    return {
+        fmt::formatted_size(fmt::runtime(fmt_str_), args...),
+        [&](auto buf, auto len) {
+          auto res =
+              fmt::format_to_n(buf, len, fmt::runtime(fmt_str_), args...);
+          FOLLY_SAFE_DCHECK(len == res.size);
+        }};
+  }
+};
+
+} // namespace ext
+
+/// A message that can automatically capture source locations. Usage:
+///   rich_msg msg1{"error: {} at {}", code, line};
+class rich_msg {
+ private:
+  exception_shared_string msg_;
+  source_location loc_;
+
+ public:
+  // Writing `rich_error{"fmt {} str {}", 1, 2}` auto-captures the location.
+  //
+  // Future: Could make implicit. Would still need curly braces to construct:
+  //  []() -> rich_msg { return "simple message"; } // error
+  //  []() -> rich_msg { return {"simple message"}; } // ok
+  template <typename... As>
+  explicit rich_msg(
+      ext::format_string_and_location<std::type_identity_t<As>...> snl,
+      As const&... args)
+      : msg_{snl.as_exception_shared_string(args...)},
+        loc_{std::move(snl.loc_)} {}
+
+  // This overload exists specifically for use in `immortal_rich_error`, which
+  // passes all of the error's constructor arguments as template parameters.
+  // In C++20, this imposes severe constraints.  We cannot pass:
+  //   - `source_location` since it's not a structural type.
+  //   - `rich_msg`, since it's not structural (and can likely never be).
+  //   - String literals, or `literal_c_str` because `char*` is forbidden in
+  //     template parameters.
+  //   - `literal_string`, because that instance would not have static storage
+  //     and thus we could use its `c_str()` in this `consteval` code.
+  //   - Format args, since our `fmt` isn't `constexpr` (yet).
+  //
+  // Implicitly converting from a `"foo"_litv` is about as good as this can get.
+  template <literal_string Str>
+  /* implicit */ consteval rich_msg(vtag_t<Str>)
+      : msg_{literal_c_str{Str.c_str()}}, loc_{} {}
+
+  rich_msg(exception_shared_string msg, source_location loc)
+      : msg_{std::move(msg)}, loc_{std::move(loc)} {}
+
+  constexpr const source_location& location() const noexcept { return loc_; }
+  constexpr const char* message() const noexcept { return msg_.what(); }
+};
+
+} // namespace folly
diff --git a/folly/folly/result/try.h b/folly/folly/result/try.h
--- a/folly/folly/result/try.h
+++ b/folly/folly/result/try.h
@@ -20,6 +20,7 @@
 #include <folly/result/result.h>
 
 /// `result<T>` <-> `Try<T>` conversions to aid in migrating legacy `Try` code.
+/// See `design_notes.md` for reasons to migrate away from `Try`.
 ///
 /// Perfect interconversion is not always possible:
 ///   - `Try` does not support reference types, but `result` does.
@@ -32,7 +33,7 @@
 
 // NB: If `T` is a reference type, this will fail with a `Try` static assert.
 template <typename T>
-Try<T> result_to_try(result<T> r) noexcept(
+[[nodiscard]] Try<T> result_to_try(result<T> r) noexcept(
     std::is_nothrow_move_constructible_v<T>) {
   if (r.has_value()) {
     if constexpr (std::is_void_v<T>) {
@@ -41,14 +42,16 @@
       return Try<T>{std::move(r).value_or_throw()};
     }
   } else {
-    return Try<T>{std::move(r).non_value().get_legacy_error_or_cancellation()};
+    return Try<T>{
+        std::move(r).error_or_stopped().get_legacy_error_or_cancellation_slow(
+            detail::result_private_t{})};
   }
 }
 
 inline constexpr struct empty_try_as_error_t {
   template <typename T>
   result<T> on_empty_try() const {
-    return {non_value_result{UsingUninitializedTry{}}};
+    return {error_or_stopped{UsingUninitializedTry{}}};
   }
 } empty_try_as_error;
 
@@ -78,9 +81,12 @@
       return {std::move(t).value()};
     }
   } else if (t.hasException()) {
-    return {non_value_result::make_legacy_error_or_cancellation(
-        std::move(t).exception())};
+    return {error_or_stopped::make_legacy_error_or_cancellation_slow(
+        detail::result_private_t{}, std::move(t).exception())};
   } else {
+    // NEVER use `private_rich_exception_ptr_sigil` here, that is reserved for
+    // a potential `Try`-in-terms-of-`result` implementation, and would make
+    // the returned `result` behave in unexpected ways.
     return std::move(if_empty).template on_empty_try<T>();
   }
 }
diff --git a/folly/folly/result/value_only_result.h b/folly/folly/result/value_only_result.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/value_only_result.h
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// Can make lighter: only shares `result_ref_wrap`, `Align.h`
+#include <folly/result/result.h>
+
+/// See `result.h` for detailed API docs.
+///
+/// See `value_only_result_coro.h` if you need to await-unwrap these from
+/// inside `result` or task coros.
+///
+/// This API is a partial mirror of `result<T>`, so comments here are minimal
+/// to reduce copy-pasta. The main differences are:
+///   - It is statically guaranteed to be in the `has_value() == true` state.
+///   - Provides `value_only()`, equivalent to `value_or_throw()`, for those
+///     scenarios where you wish to assert you have a `value_only_result`.
+///   - Omits some APIs (for now) -- see "Potential extensions"
+///
+/// A major motivation for `value_only_result` is to enable uniform APIs in
+/// `folly::coro::collect`.  Specifically:
+///   - The same generic lambda can handle values from both throwing and
+///     non-throwing inputs.
+///   - It is easy to specify that an adaptor or sink expects errors to be
+///     swallowed before it is reached.
+/// It similarly also enables other generic code to handle both fallible and
+/// infallible inputs.
+///
+/// ## Potential extensions
+///
+/// Features are added as-needed ...  and some should never be needed.  If you
+/// need it, it is definitely fine to support `co_yield co_result(valOnlyRes)`
+/// from async tasks.
+///
+/// It's a good idea to add a linter that errors whenever a function returning
+/// `value_only_result` is not `noexcept`.
+///
+/// While it is hard to imagine a use-case that requires `value_only_result` to
+/// **be** a coroutine, here are some notes:
+///   - The `noexcept` linter above becomes more important, since writing
+///     `unhandled_exception() { throw; }` neither makes sense, nor bodes well
+///     for well-optimized code.  But writing `unhandled_exception() noexcept`
+///     definitely requires a pervasive linter.
+///   - You may want to add a `::template apply<>` to `result`-like types
+///     to make it easier for users to write generic coros that are
+///     either-`result`-or-`value_only_result`.
+///
+/// Implementation note: It wouldn't be hard to deduplicate most of the API
+/// implementation with `result.h`, but I went with the "copy" route since it
+/// keeps down the complexity of the main file -- and this here isn't too bad.
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+template <typename T = void>
+class value_only_result;
+
+namespace detail {
+
+// Shared implementation for `T` non-`void` and `void`
+template <typename Derived, typename T>
+class value_only_result_crtp {
+  static_assert(!std::is_same_v<error_or_stopped, std::remove_cvref_t<T>>);
+  static_assert(!std::is_same_v<stopped_result_t, std::remove_cvref_t<T>>);
+  // `value_only_result` is used on non-throwing code paths, so we require `T`
+  // to be noexcept-movable to avoid hidden termination or exception risks.
+  static_assert(
+      std::is_void_v<T> || std::is_reference_v<T> ||
+          (std::is_nothrow_move_constructible_v<T> &&
+           std::is_nothrow_move_assignable_v<T>),
+      "value_only_result<T> requires T to be noexcept-movable");
+
+ protected:
+  using storage_type = detail::result_ref_wrap<lift_unit_t<T>>;
+  static_assert(!std::is_reference_v<storage_type>);
+
+  storage_type value_;
+
+  template <typename>
+  friend class folly::value_only_result; // The simple conversion uses `value_`
+
+  struct private_copy_t {};
+  value_only_result_crtp(private_copy_t, const Derived& that)
+      : value_(that.value_) {}
+
+  template <typename V>
+  value_only_result_crtp(std::in_place_t, V&& v)
+      : value_(static_cast<V&&>(v)) {}
+
+  ~value_only_result_crtp() = default;
+
+ public:
+  using value_type = T; // NB: can be a reference
+
+  /// Movable, so long as `T` is.
+  value_only_result_crtp(value_only_result_crtp&&) = default;
+  value_only_result_crtp& operator=(value_only_result_crtp&&) = default;
+
+  /// Explicit `.copy()` method instead of a standard copy constructor.
+  Derived copy()
+    requires(
+        !std::is_rvalue_reference_v<T> &&
+        (std::is_void_v<T> || std::is_copy_constructible_v<T>))
+  {
+    return Derived{private_copy_t{}, static_cast<const Derived&>(*this)};
+  }
+  // The `const`-safety constraints are explained on `result::copy`.
+  Derived copy() const
+    requires(
+        (!std::is_reference_v<T> ||
+         std::is_const_v<std::remove_reference_t<T>>) &&
+        (std::is_void_v<T> || std::is_copy_constructible_v<T>))
+  {
+    return Derived{private_copy_t{}, static_cast<const Derived&>(*this)};
+  }
+  // IMPORTANT: Read the `result` copy ctor comment before touching.
+  value_only_result_crtp(const value_only_result_crtp&) = delete;
+  value_only_result_crtp& operator=(const value_only_result_crtp&) = delete;
+
+  // NB: This is currently missing the counterpart to "Fallible copy/move
+  // conversion" from `result.h`.  That's because it seems very unlikely that
+  // types would have implicit `U1` -> `value_only_result<U2>` conversions.
+
+  [[nodiscard]] bool has_value() const { return true; }
+  [[nodiscard]] bool has_stopped() const { return false; }
+
+  // Only provide `==` since less/greater doesn't make sense for `result`.
+  bool operator==(const value_only_result_crtp&) const = default;
+};
+
+} // namespace detail
+
+// The default specialization is non-`void`, but `value_only_result<>` defaults
+// to `void`.
+template <typename T>
+class [[nodiscard]]
+// Not a coroutine, but any reasonable implementation would be elidable.
+[[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] value_only_result final
+    : public detail::value_only_result_crtp<value_only_result<T>, T> {
+ private:
+  using base = detail::value_only_result_crtp<value_only_result<T>, T>;
+  // For `T` non-`void`, we store either `T` or a ref wrapper.
+  using ref_wrapped_t = typename base::storage_type;
+
+ public:
+  using detail::value_only_result_crtp<value_only_result<T>, T>::
+      value_only_result_crtp;
+
+  /// Future: add default-constructibility iff `result` gets it.
+
+  /// Copy- & move-conversion from a reference wrapper.
+  /* implicit */ value_only_result(ref_wrapped_t t) noexcept
+    requires std::is_reference_v<T>
+      : base{std::in_place, std::move(t)} {}
+
+  /// Move-construct `value_only_result<T>` from the underlying value type `T`.
+  /* implicit */ value_only_result(T&& t) noexcept(
+      noexcept(ref_wrapped_t(std::move(t))))
+    requires(
+        !std::is_reference_v<T> && std::is_constructible_v<ref_wrapped_t, T &&>)
+      : base{std::in_place, std::move(t)} {}
+  value_only_result& operator=(T&& t) noexcept(
+      std::is_nothrow_assignable_v<ref_wrapped_t, T&&>)
+    requires(
+        !std::is_reference_v<T> && std::is_assignable_v<ref_wrapped_t, T &&>)
+  {
+    this->value_ = std::move(t);
+    return *this;
+  }
+
+  /// Copy underlying `T`, but ONLY when small & trivially copyable.
+  /* implicit */ value_only_result(const T& t) noexcept(
+      noexcept(ref_wrapped_t(t)))
+    requires(
+        !std::is_reference_v<T> &&
+        std::is_constructible_v<ref_wrapped_t, const T&> &&
+        std::is_trivially_copyable_v<T> &&
+        sizeof(T) <= hardware_constructive_interference_size)
+      : base{std::in_place, t} {}
+
+  /// No copy assignment, just like `result`.
+
+  /// Simple copy/move conversion.
+  ///
+  /// Convert `value_only_result<U>` to `value_only_result<T>` if:
+  ///   - `U` is a value type that is copy/move convertible to `T`.
+  ///   - `U` is a reference whose ref-wrapper is converible to `T`.
+  template <class Arg, typename ResultT = std::remove_cvref_t<Arg>>
+    requires(
+        !std::is_same_v<ResultT, value_only_result> && // Not a move/copy ctor
+        std::is_constructible_v<
+            ref_wrapped_t,
+            typename ResultT::storage_type &&>)
+  /* implicit */ value_only_result(Arg&& that)
+      : base{std::in_place, std::forward<Arg>(that).value_} {
+    static_assert(is_instantiation_of_v<value_only_result, ResultT>);
+  }
+
+  /// Retrieve non-reference `T` -- `value_or_throw` is a synonym of
+  /// `value_only`
+
+  [[nodiscard]] const T& value_or_throw() const& noexcept
+    requires(!std::is_reference_v<T>)
+  {
+    return this->value_;
+  }
+  [[nodiscard]] const T& value_only() const& noexcept
+    requires(!std::is_reference_v<T>)
+  {
+    return this->value_;
+  }
+
+  [[nodiscard]] T& value_or_throw() & noexcept
+    requires(!std::is_reference_v<T>)
+  {
+    return this->value_;
+  }
+  [[nodiscard]] T& value_only() & noexcept
+    requires(!std::is_reference_v<T>)
+  {
+    return this->value_;
+  }
+
+  [[nodiscard]] const T&& value_or_throw() const&& noexcept
+    requires(!std::is_reference_v<T>)
+  {
+    return std::move(this->value_);
+  }
+  [[nodiscard]] const T&& value_only() const&& noexcept
+    requires(!std::is_reference_v<T>)
+  {
+    return std::move(this->value_);
+  }
+
+  [[nodiscard]] T&& value_or_throw() && noexcept
+    requires(!std::is_reference_v<T>)
+  {
+    return std::move(this->value_);
+  }
+  [[nodiscard]] T&& value_only() && noexcept
+    requires(!std::is_reference_v<T>)
+  {
+    return std::move(this->value_);
+  }
+
+  /// Retrieve reference `T` -- `value_or_throw` is a synonym of `value_only`
+  ///
+  /// NB Unlike the value-type versions, accesors cannot mutate the reference
+  /// wrapper inside `this`.  Assign a ref-wrapper to the `result` to do that.
+
+  /// Lvalue result-ref propagate `const`: `const result<T&>` -> `const T&`.
+  /// See a discussion of the trade-offs in `docs/result.md`.
+
+  [[nodiscard]] like_t<const int&, T> value_or_throw() const& noexcept
+    requires std::is_lvalue_reference_v<T>
+  {
+    return std::as_const(this->value_.get());
+  }
+  [[nodiscard]] like_t<const int&, T> value_only() const& noexcept
+    requires std::is_lvalue_reference_v<T>
+  {
+    return std::as_const(this->value_.get());
+  }
+
+  [[nodiscard]] T value_or_throw() & noexcept
+    requires std::is_lvalue_reference_v<T>
+  {
+    return this->value_.get();
+  }
+
+  [[nodiscard]] T value_only() & noexcept
+    requires std::is_lvalue_reference_v<T>
+  {
+    return this->value_.get();
+  }
+
+  [[nodiscard]] T value_only() && noexcept
+    requires std::is_lvalue_reference_v<T>
+  {
+    return this->value_.get();
+  }
+  [[nodiscard]] T value_or_throw() && noexcept
+    requires std::is_lvalue_reference_v<T>
+  {
+    return this->value_.get();
+  }
+
+  // R-value refs follow `folly::rvalue_reference_wrapper`.  They model
+  // single-use references, and thus require `&&` qualification.
+
+  [[nodiscard]] T value_or_throw() && noexcept
+    requires std::is_rvalue_reference_v<T>
+  {
+    return std::move(this->value_).get();
+  }
+  [[nodiscard]] T value_only() && noexcept
+    requires std::is_rvalue_reference_v<T>
+  {
+    return std::move(this->value_).get();
+  }
+
+  explicit operator result<T>() && noexcept { // T is nothrow-movable
+    return result<T>{std::move(*this).value_or_throw()};
+  }
+};
+
+template <typename T>
+value_only_result(std::reference_wrapper<T>) -> value_only_result<T&>;
+
+template <typename T>
+value_only_result(rvalue_reference_wrapper<T>) -> value_only_result<T&&>;
+
+// Specialization for `T = void` aka `value_only_result<>`.
+template <>
+class [[nodiscard]]
+// Not yet a coroutine, but if we make it one, it SHOULD be elidable.
+[[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] value_only_result<void>
+    final
+    : public detail::value_only_result_crtp<value_only_result<void>, void> {
+ private:
+  using base = detail::value_only_result_crtp<value_only_result<void>, void>;
+
+ public:
+  using base::value_only_result_crtp;
+
+  value_only_result() : base(std::in_place, unit) {}
+
+  void value_or_throw() const noexcept {}
+  void value_only() const noexcept {}
+
+  explicit operator result<void>() const noexcept { return result<void>{}; }
+};
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/result/value_only_result_coro.h b/folly/folly/result/value_only_result_coro.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/result/value_only_result_coro.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/result/coro.h>
+#include <folly/result/value_only_result.h>
+
+/// See `value_only_result.h` for why this type exists.
+/// See `coro.h` for the `or_unwind` docs`.
+///
+/// WARNING: `auto&& ref = co_await or_unwind(rvalueFn())` dangles; search
+/// `result.md` for "LLVM issue #177023".  Safe: `auto val = ...`
+
+#if FOLLY_HAS_RESULT
+
+namespace folly {
+
+// Making these `final` makes `unsafe_mover` simpler due to no slicing risk.
+
+// `co_await or_unwind(valueOnlyResFn())` returns rvalue reference
+template <typename T>
+class [[nodiscard]] or_unwind<value_only_result<T>&&> final
+    : public detail::result_or_unwind<value_only_result<T>&&> {
+  using detail::result_or_unwind<value_only_result<T>&&>::result_or_unwind;
+};
+template <typename T>
+or_unwind(value_only_result<T>&&) -> or_unwind<value_only_result<T>&&>;
+
+// `co_await or_unwind(res)` returns lvalue reference
+template <typename T>
+class [[nodiscard]] or_unwind<value_only_result<T>&> final
+    : public detail::result_or_unwind<value_only_result<T>&> {
+  using detail::result_or_unwind<value_only_result<T>&>::result_or_unwind;
+};
+template <typename T>
+or_unwind(value_only_result<T>&) -> or_unwind<value_only_result<T>&>;
+
+// `co_await or_unwind(std::as_const(res))` returns lvalue reference to const
+template <typename T>
+class [[nodiscard]] or_unwind<const value_only_result<T>&> final
+    : public detail::result_or_unwind<const value_only_result<T>&> {
+  using detail::result_or_unwind<const value_only_result<T>&>::result_or_unwind;
+};
+template <typename T>
+or_unwind(const value_only_result<T>&)
+    -> or_unwind<const value_only_result<T>&>;
+
+/// `or_unwind_owning(r)` takes ownership of `r`, while `co_await` returns it.
+///
+/// Moves the result into the awaitable, unlike `or_unwind` which holds a ref:
+///   auto my_unwind() { return or_unwind_owning{value_only_result<T>{...}}; }
+/// With `or_unwind`, this would have been a use-after-stack error.
+template <typename T>
+class [[nodiscard]] or_unwind_owning<value_only_result<T>> final
+    : public detail::result_or_unwind_owning<value_only_result<T>> {
+  using detail::result_or_unwind_owning<
+      value_only_result<T>>::result_or_unwind_owning;
+};
+template <typename T>
+or_unwind_owning(value_only_result<T>)
+    -> or_unwind_owning<value_only_result<T>>;
+
+} // namespace folly
+
+#endif // FOLLY_HAS_RESULT
diff --git a/folly/folly/settings/Observer.h b/folly/folly/settings/Observer.h
--- a/folly/folly/settings/Observer.h
+++ b/folly/folly/settings/Observer.h
@@ -36,19 +36,6 @@
 template <typename T, std::atomic<uint64_t>* Ptr, typename Tag>
 observer::Observer<T> getObserver(
     settings::detail::SettingWrapper<T, Ptr, Tag> setting) {
-  // Make observable a unique_ptr so it can be moved and captured in the setting
-  // update callback
-  auto observable = std::make_unique<observer::SimpleObservable<T>>(*setting);
-  auto observer = observable->getObserver();
-
-  auto callbackHandle = setting.addCallback(
-      [observable = std::move(observable)](const auto& newContents) {
-        observable->setValue(newContents.value);
-      });
-  // Create a wrapped observer to capture the callback handle and keep it alive
-  // as long as the observer is alive
-  return observer::makeObserver(
-      [callbackHandle = std::move(callbackHandle),
-       observer = std::move(observer)]() { return **observer; });
+  return setting.observer();
 }
 } // namespace folly::settings
diff --git a/folly/folly/settings/Settings.cpp b/folly/folly/settings/Settings.cpp
--- a/folly/folly/settings/Settings.cpp
+++ b/folly/folly/settings/Settings.cpp
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-#include <fmt/format.h>
 #include <folly/settings/Settings.h>
 
-#include <map>
+#include <unordered_map>
 
 #include <folly/Synchronized.h>
 
@@ -32,9 +31,10 @@
 void registerSetting(SettingCoreBase& core) {
   if (core.meta().project.empty() ||
       core.meta().project.find('_') != std::string::npos) {
-    throw std::logic_error(fmt::format(
-        "Setting project must be nonempty and cannot contain underscores: {}",
-        core.meta().project));
+    throw std::logic_error(
+        fmt::format(
+            "Setting project must be nonempty and cannot contain underscores: {}",
+            core.meta().project));
   }
 
   auto fullname = fmt::format("{}_{}", core.meta().project, core.meta().name);
diff --git a/folly/folly/settings/Settings.h b/folly/folly/settings/Settings.h
--- a/folly/folly/settings/Settings.h
+++ b/folly/folly/settings/Settings.h
@@ -84,6 +84,18 @@
   std::conditional_t<IsSmallPOD<T>, T, const T&> value() const {
     return operator*();
   }
+  /**
+   * Same as value() but registers this setting as an observer dependency. If
+   * this setting is used to compute an observer, subsequent setting updates
+   * will trigger the recomputation of that observer.
+   */
+  std::conditional_t<IsSmallPOD<T>, T, const T&>
+  valueRegisterObserverDependency() {
+    if (FOLLY_UNLIKELY(observer_detail::ObserverManager::inManagerThread())) {
+      registerObserverDependency();
+    }
+    return operator*();
+  }
 
   /**
    * Returns the setting's value from snapshot. Following two forms are
@@ -96,6 +108,11 @@
       const Snapshot& snapshot) const;
 
   /**
+   * Returns an Observer<T> that's updated whenever this setting is updated.
+   */
+  const folly::observer::Observer<T>& observer() { return core_.observer(); }
+
+  /**
    * Atomically updates the setting's current value. The next call to
    * operator*() will invalidate all references returned by previous calls to
    * operator*() on that thread.
@@ -145,108 +162,90 @@
    */
   std::string_view updateReason(const Snapshot& snapshot) const;
 
+  /**
+   * Returns the setting's name
+   */
+  std::string_view name() const { return core_.meta().name; }
+
+  /**
+   * Returns the setting's project
+   */
+  std::string_view project() const { return core_.meta().project; }
+
+  /**
+   * Returns the setting's description
+   */
+  std::string_view description() const { return core_.meta().description; }
+
   explicit SettingWrapper(SettingCore<T, Tag>& core) : core_(core) {}
 
  private:
+  FOLLY_NOINLINE void registerObserverDependency() { observer().getSnapshot(); }
+
   SettingCore<T, Tag>& core_;
   friend class folly::settings::Snapshot;
 };
 
-/**
- * Optimization: fast-path on top of the Meyers singleton. Each
- * translation unit gets this code inlined, while the slow path
- * initialization code is not.  We check the global pointer which
- * should only be initialized after the Meyers singleton. It's ok for
- * multiple calls to attempt to update the global pointer, as they
- * would be serialized on the Meyer's singleton initialization lock
- * anyway.
- *
- * Both FOLLY_SETTING_DECLARE and FOLLY_SETTING_DEFINE will provide
- * a copy of this function and we work around ODR by using different
- * overload types.
- *
- * Requires a trailing semicolon.
- */
-#define FOLLY_DETAIL_SETTINGS_DEFINE_LOCAL_FUNC__(                             \
-    _project, _name, _Type, _overloadType)                                     \
-  struct FOLLY_SETTINGS_TAG__##_project##_##_name;                             \
-  extern ::std::atomic<::folly::settings::detail::SettingCore<                 \
-      _Type,                                                                   \
-      FOLLY_SETTINGS_TAG__##_project##_##_name>*>                              \
-      FOLLY_SETTINGS_CACHE__##_project##_##_name;                              \
-  extern ::std::atomic<uint64_t> FOLLY_SETTINGS_TRIVIAL__##_project##_##_name; \
-  ::folly::settings::detail::                                                  \
-      SettingCore<_Type, FOLLY_SETTINGS_TAG__##_project##_##_name>&            \
-          FOLLY_SETTINGS_FUNC__##_project##_##_name();                         \
-  FOLLY_ALWAYS_INLINE auto FOLLY_SETTINGS_LOCAL_FUNC__##_project##_##_name(    \
-      _overloadType) {                                                         \
-    auto* folly_detail_settings_value =                                        \
-        FOLLY_SETTINGS_CACHE__##_project##_##_name.load(                       \
-            ::std::memory_order_acquire);                                      \
-    if (FOLLY_UNLIKELY(!folly_detail_settings_value)) {                        \
-      folly_detail_settings_value =                                            \
-          &FOLLY_SETTINGS_FUNC__##_project##_##_name();                        \
-      FOLLY_SETTINGS_CACHE__##_project##_##_name.store(                        \
-          folly_detail_settings_value, ::std::memory_order_release);           \
-    }                                                                          \
-    return ::folly::settings::detail::SettingWrapper<                          \
-        _Type,                                                                 \
-        &FOLLY_SETTINGS_TRIVIAL__##_project##_##_name,                         \
-        FOLLY_SETTINGS_TAG__##_project##_##_name>(                             \
-        *folly_detail_settings_value);                                         \
-  }                                                                            \
-  /* This is here just to force a semicolon */                                 \
-  ::folly::settings::detail::                                                  \
-      SettingCore<_Type, FOLLY_SETTINGS_TAG__##_project##_##_name>&            \
-          FOLLY_SETTINGS_FUNC__##_project##_##_name()
-
+template <
+    typename T,
+    std::atomic<uint64_t>* TrivialPtr,
+    typename Tag,
+    std::atomic<SettingCore<T, Tag>*>& CachedCore,
+    SettingCore<T, Tag>& (*Func)()>
+struct Accessor {
+  /**
+   * Optimization: fast-path on top of the Meyers singleton. We check the global
+   * pointer which should only be initialized after the Meyers singleton. It's
+   * ok for multiple calls to attempt to update the global pointer, as they
+   * would be serialized on the Meyer's singleton initialization lock anyway.
+   */
+  FOLLY_ALWAYS_INLINE SettingWrapper<T, TrivialPtr, Tag> operator()() {
+    auto* core = CachedCore.load(std::memory_order_acquire);
+    if (FOLLY_UNLIKELY(!core)) {
+      core = &Func();
+      CachedCore.store(core, std::memory_order_release);
+    }
+    return SettingWrapper<T, TrivialPtr, Tag>(*core);
+  }
+};
 } // namespace detail
 
+#if defined(_MSC_VER)
+// MSVC does not support section attributes
+#define FOLLY_SETTINGS_DETAIL_SECTION_ATTRIBUTE /* nothing */
+#elif defined(__APPLE__)
+// Mach-O: section attribute needs segment,section
+#define FOLLY_SETTINGS_DETAIL_SECTION_ATTRIBUTE \
+  gnu::section("__DATA,.folly.settings")
+#else
+// ELF: section attribute just needs section name
+#define FOLLY_SETTINGS_DETAIL_SECTION_ATTRIBUTE \
+  gnu::section(".folly.settings.cache")
+#endif
+
 /**
- * Defines a setting.
- *
- * Settings are either mutable or immutable where mutable setting values can
- * change at runtime whereas immutable setting values can not be changed after
- * the setting project is frozen (see Immutables.h).
- *
- * FOLLY_SETTING_DEFINE() can only be placed in a single translation unit
- * and will be checked against accidental collisions.
- *
- * The setting API can be accessed via FOLLY_SETTING(project, name).<api_func>()
- * and is documented in the Setting class.
- *
- * All settings for a common namespace; (project, name) must be unique
- * for the whole program.  Collisions are verified at runtime on
- * program startup.
- *
- * @param _project  Project identifier, can only contain [a-zA-Z0-9]
- * @param _name  setting name within the project, can only contain [_a-zA-Z0-9].
- *   The string "<project>_<name>" must be unique for the whole program.
- * @param _Type  setting value type
- * @param _def   default value for the setting
- * @param _mut   mutability of the setting
- * @param _cli   determines if a setting can be set through the command line
- * @param _desc  setting documentation
+ * Registers a setting without defining an Accessor. This should rarely be used
+ * directly and most use cases will want FOLLY_SETTING_DEFINE instead.
  */
-#define FOLLY_SETTING_DEFINE(_project, _name, _Type, _def, _mut, _cli, _desc) \
+#define FOLLY_SETTING_REGISTER(                                               \
+    _project, _name, _Type, _def, _mut, _cli, _desc)                          \
   struct FOLLY_SETTINGS_TAG__##_project##_##_name {};                         \
   /* Fastpath optimization, see notes in FOLLY_SETTINGS_DEFINE_LOCAL_FUNC__.  \
      Aggregate all off these together in a single section for better TLB      \
      and cache locality. */                                                   \
-  __attribute__((__section__(".folly.settings.cache"))) ::std::atomic<        \
+  [[FOLLY_SETTINGS_DETAIL_SECTION_ATTRIBUTE]] [[maybe_unused]] ::std::atomic< \
       ::folly::settings::detail::                                             \
           SettingCore<_Type, FOLLY_SETTINGS_TAG__##_project##_##_name>*>      \
       FOLLY_SETTINGS_CACHE__##_project##_##_name;                             \
   /* Location for the small value cache (if _Type is small and trivial).      \
      Intentionally located right after the pointer cache above to take        \
      advantage of the prefetching */                                          \
-  __attribute__((                                                             \
-      __section__(".folly.settings.cache"))) ::std::atomic<uint64_t>          \
+  [[FOLLY_SETTINGS_DETAIL_SECTION_ATTRIBUTE]] ::std::atomic<::std::uint64_t>  \
       FOLLY_SETTINGS_TRIVIAL__##_project##_##_name;                           \
   /* Meyers singleton to avoid SIOF */                                        \
   FOLLY_NOINLINE ::folly::settings::detail::                                  \
       SettingCore<_Type, FOLLY_SETTINGS_TAG__##_project##_##_name>&           \
-          FOLLY_SETTINGS_FUNC__##_project##_##_name() {                       \
+      FOLLY_SETTINGS_FUNC__##_project##_##_name() {                           \
     static ::folly::Indestructible<::folly::settings::detail::SettingCore<    \
         _Type,                                                                \
         FOLLY_SETTINGS_TAG__##_project##_##_name>>                            \
@@ -266,14 +265,64 @@
   }                                                                           \
   /* Ensure the setting is registered even if not used in program */          \
   auto& FOLLY_SETTINGS_INIT__##_project##_##_name =                           \
-      FOLLY_SETTINGS_FUNC__##_project##_##_name();                            \
-  FOLLY_DETAIL_SETTINGS_DEFINE_LOCAL_FUNC__(_project, _name, _Type, char)
+      FOLLY_SETTINGS_FUNC__##_project##_##_name()
+/**
+ * Defines a setting.
+ *
+ * Settings are either mutable or immutable where mutable setting values can
+ * change at runtime whereas immutable setting values can not be changed after
+ * the setting project is frozen (see Immutables.h).
+ *
+ * FOLLY_SETTING_DEFINE() can only be placed in a single translation unit
+ * and will be checked against accidental collisions.
+ *
+ * The setting API can be accessed via FOLLY_SETTING(project, name).<api_func>()
+ * and is documented in the Setting class.
+ *
+ * All settings for a common namespace; (project, name) must be unique
+ * for the whole program.  Collisions are verified at runtime on
+ * program startup.
+ *
+ * @param _project  Project identifier, can only contain [a-zA-Z0-9]
+ * @param _name  setting name within the project, can only contain [_a-zA-Z0-9].
+ *   The string "<project>_<name>" must be unique for the whole program.
+ * @param _Type  setting value type
+ * @param _def   default value for the setting
+ * @param _mut   mutability of the setting
+ * @param _cli   determines if a setting can be set through the command line
+ * @param _desc  setting documentation
+ */
+#define FOLLY_SETTING_DEFINE(_project, _name, _Type, _def, _mut, _cli, _desc) \
+  FOLLY_SETTING_REGISTER(_project, _name, _Type, _def, _mut, _cli, _desc);    \
+  ::folly::settings::detail::Accessor<                                        \
+      _Type,                                                                  \
+      &FOLLY_SETTINGS_TRIVIAL__##_project##_##_name,                          \
+      FOLLY_SETTINGS_TAG__##_project##_##_name,                               \
+      FOLLY_SETTINGS_CACHE__##_project##_##_name,                             \
+      FOLLY_SETTINGS_FUNC__##_project##_##_name>                              \
+      FOLLY_SETTINGS_ACCESSOR__##_project##_##_name
 
 /**
  * Declares a setting that's defined elsewhere.
  */
-#define FOLLY_SETTING_DECLARE(_project, _name, _Type) \
-  FOLLY_DETAIL_SETTINGS_DEFINE_LOCAL_FUNC__(_project, _name, _Type, int)
+#define FOLLY_SETTING_DECLARE(_project, _name, _Type)               \
+  struct FOLLY_SETTINGS_TAG__##_project##_##_name;                  \
+  extern ::std::atomic<::folly::settings::detail::SettingCore<      \
+      _Type,                                                        \
+      FOLLY_SETTINGS_TAG__##_project##_##_name>*>                   \
+      FOLLY_SETTINGS_CACHE__##_project##_##_name;                   \
+  extern ::std::atomic<::std::uint64_t>                             \
+      FOLLY_SETTINGS_TRIVIAL__##_project##_##_name;                 \
+  ::folly::settings::detail::                                       \
+      SettingCore<_Type, FOLLY_SETTINGS_TAG__##_project##_##_name>& \
+      FOLLY_SETTINGS_FUNC__##_project##_##_name();                  \
+  extern ::folly::settings::detail::Accessor<                       \
+      _Type,                                                        \
+      &FOLLY_SETTINGS_TRIVIAL__##_project##_##_name,                \
+      FOLLY_SETTINGS_TAG__##_project##_##_name,                     \
+      FOLLY_SETTINGS_CACHE__##_project##_##_name,                   \
+      FOLLY_SETTINGS_FUNC__##_project##_##_name>                    \
+      FOLLY_SETTINGS_ACCESSOR__##_project##_##_name
 
 /**
  * Accesses a defined setting.
@@ -285,7 +334,7 @@
  *     the setting itself.
  */
 #define FOLLY_SETTING(_project, _name) \
-  FOLLY_SETTINGS_LOCAL_FUNC__##_project##_##_name(0)
+  FOLLY_SETTINGS_ACCESSOR__##_project##_##_name()
 
 /**
  * @return If the setting exists, returns the current settings metadata.
diff --git a/folly/folly/settings/Types.h b/folly/folly/settings/Types.h
--- a/folly/folly/settings/Types.h
+++ b/folly/folly/settings/Types.h
@@ -113,7 +113,7 @@
 template <typename T>
 Expected<Unit, ExpectedErrorType<decltype(tryTo<T>(StringPiece{}))>> convertTo(
     const SettingValueAndMetadata& src, T& out) {
-  auto result = tryTo<T>(src.value);
+  auto result = tryTo<T>(StringPiece(src.value));
   if (result.hasError()) {
     return makeUnexpected(std::move(result).error());
   }
diff --git a/folly/folly/settings/detail/SettingsImpl.h b/folly/folly/settings/detail/SettingsImpl.h
--- a/folly/folly/settings/detail/SettingsImpl.h
+++ b/folly/folly/settings/detail/SettingsImpl.h
@@ -23,18 +23,24 @@
 #include <type_traits>
 #include <unordered_map>
 
+#include <fmt/format.h>
+
 #include <folly/Conv.h>
 #include <folly/Function.h>
 #include <folly/Optional.h>
 #include <folly/Portability.h>
 #include <folly/SharedMutex.h>
 #include <folly/ThreadLocal.h>
+#include <folly/Traits.h>
 #include <folly/Utility.h>
 #include <folly/concurrency/SingletonRelaxedCounter.h>
 #include <folly/container/F14Set.h>
 #include <folly/lang/Aligned.h>
+#include <folly/observer/Observer.h>
+#include <folly/observer/SimpleObservable.h>
 #include <folly/settings/Immutables.h>
 #include <folly/settings/Types.h>
+#include <folly/synchronization/DelayedInit.h>
 #include <folly/synchronization/RelaxedAtomic.h>
 
 namespace folly {
@@ -510,6 +516,20 @@
   return contents.updateReason;
 }
 
+template <typename F>
+struct NamedObserverCreator {
+  NamedObserverCreator(std::string name, F&& creator)
+      : name_(std::move(name)), creator_(std::forward<F>(creator)) {}
+
+  auto operator()() { return creator_(); }
+
+  const std::string& getName() const { return name_; }
+
+ private:
+  std::string name_;
+  F creator_;
+};
+
 template <class T, typename Tag>
 class SettingCore : public TypedSettingCore<T> {
  public:
@@ -553,6 +573,13 @@
     return CallbackHandle(std::move(callbackPtr), *this);
   }
 
+  /**
+   * Returns an Observer<T> that's updated whenever this setting is updated.
+   */
+  const observer::Observer<T>& observer() {
+    return observer_.try_emplace_with([this]() { return createObserver(); });
+  }
+
   SettingCore(
       SettingMetadata meta,
       T defaultValue,
@@ -568,6 +595,7 @@
 
   F14FastSet<std::shared_ptr<UpdateCallback>> callbacks_;
   std::atomic<bool> hasHadCallbacks_{false};
+  DelayedInit<observer::Observer<T>> observer_;
 
   void setImpl(
       const T& t, std::string_view reason, SnapshotBase* snapshot) override {
@@ -613,6 +641,33 @@
     for (auto& callbackPtr : callbacksSnapshot) {
       auto& callback = *callbackPtr;
       callback(contents);
+    }
+  }
+  /**
+   * Creates a folly::observer::Observer<T> for this setting that's updated
+   * whenever this setting is updated.
+   */
+  observer::Observer<T> createObserver() {
+    // Make observable a unique_ptr so it can be moved and captured in the
+    // setting update callback
+    auto setting = this->getWithHint(this->trivialStorage_);
+    auto observable = std::make_unique<observer::SimpleObservable<T>>(setting);
+    auto observer = observable->getObserver();
+    auto callbackHandle = addCallback(
+        [observable = std::move(observable)](const auto& newContents) {
+          observable->setValue(newContents.value);
+        });
+    // Create a wrapped observer to capture the callback handle and keep it
+    // alive as long as the observer is alive
+    auto& meta = this->meta();
+    NamedObserverCreator creator(
+        fmt::format("FOLLY_SETTING_{}_{}", meta.project, meta.name),
+        [callbackHandle = std::move(callbackHandle),
+         observer = std::move(observer)]() { return **observer; });
+    if constexpr (IsEqualityComparable<T>::value) {
+      return observer::makeValueObserver(std::move(creator));
+    } else {
+      return observer::makeObserver(std::move(creator));
     }
   }
 };
diff --git a/folly/folly/small_vector.h b/folly/folly/small_vector.h
--- a/folly/folly/small_vector.h
+++ b/folly/folly/small_vector.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/container/small_vector.h>
+#include <folly/container/small_vector.h> // @shim
diff --git a/folly/folly/sorted_vector_types.h b/folly/folly/sorted_vector_types.h
--- a/folly/folly/sorted_vector_types.h
+++ b/folly/folly/sorted_vector_types.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include <folly/container/sorted_vector_types.h>
+#include <folly/container/sorted_vector_types.h> // @shim
diff --git a/folly/folly/ssl/OpenSSLCertUtils.cpp b/folly/folly/ssl/OpenSSLCertUtils.cpp
--- a/folly/folly/ssl/OpenSSLCertUtils.cpp
+++ b/folly/folly/ssl/OpenSSLCertUtils.cpp
@@ -302,6 +302,28 @@
   return buf;
 }
 
+X509UniquePtr OpenSSLCertUtils::pemDecode(ByteRange range) {
+  BioUniquePtr bio(BIO_new_mem_buf(range.data(), range.size()));
+  if (!bio) {
+    throw std::runtime_error("failed to create BIO");
+  }
+  X509UniquePtr x509(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+  if (!x509) {
+    throw std::runtime_error("failed to read cert");
+  }
+  return x509;
+}
+
+std::string OpenSSLCertUtils::pemEncode(X509& x509) {
+  BioUniquePtr bio(BIO_new(BIO_s_mem()));
+  if (!PEM_write_bio_X509(bio.get(), &x509)) {
+    throw std::runtime_error("Failed to encode X509");
+  }
+  BUF_MEM* bptr = nullptr;
+  BIO_get_mem_ptr(bio.get(), &bptr);
+  return {bptr->data, bptr->length};
+}
+
 std::vector<X509UniquePtr> OpenSSLCertUtils::readCertsFromBuffer(
     ByteRange range) {
   BioUniquePtr b(
@@ -324,11 +346,12 @@
       // Reach end of buffer.
       break;
     }
-    throw std::runtime_error(folly::to<std::string>(
-        "Unable to parse cert ",
-        certs.size(),
-        ": ",
-        getOpenSSLErrorString(err)));
+    throw std::runtime_error(
+        folly::to<std::string>(
+            "Unable to parse cert ",
+            certs.size(),
+            ": ",
+            getOpenSSLErrorString(err)));
   }
   return certs;
 }
@@ -375,9 +398,10 @@
       auto err = ERR_get_error();
       if (ERR_GET_LIB(err) != ERR_LIB_X509 ||
           ERR_GET_REASON(err) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
-        throw std::runtime_error(folly::to<std::string>(
-            "Could not insert CA certificate into store: ",
-            getOpenSSLErrorString(err)));
+        throw std::runtime_error(
+            folly::to<std::string>(
+                "Could not insert CA certificate into store: ",
+                getOpenSSLErrorString(err)));
       }
     }
   }
diff --git a/folly/folly/ssl/OpenSSLCertUtils.h b/folly/folly/ssl/OpenSSLCertUtils.h
--- a/folly/folly/ssl/OpenSSLCertUtils.h
+++ b/folly/folly/ssl/OpenSSLCertUtils.h
@@ -103,6 +103,20 @@
   static std::unique_ptr<IOBuf> derEncode(X509&);
 
   /**
+   * Decode the PEM representation of an X509 certificate.
+   *
+   * Throws on error (if a valid certificate can't be decoded).
+   */
+  static X509UniquePtr pemDecode(ByteRange);
+
+  /**
+   * Encode an X509 certificate in PEM format.
+   *
+   * Throws on error.
+   */
+  static std::string pemEncode(X509&);
+
+  /**
    * Read certificates from memory and returns them as a vector of X509
    * pointers. Throw if there is any malformed cert or memory allocation
    * problem.
diff --git a/folly/folly/ssl/OpenSSLHash.cpp b/folly/folly/ssl/OpenSSLHash.cpp
--- a/folly/folly/ssl/OpenSSLHash.cpp
+++ b/folly/folly/ssl/OpenSSLHash.cpp
@@ -24,8 +24,9 @@
 
 [[noreturn]] void OpenSSLHash::check_out_size_throw(
     size_t size, MutableByteRange out) {
-  throw std::invalid_argument(fmt::format(
-      "expected out of size {} but was of size {}", size, out.size()));
+  throw std::invalid_argument(
+      fmt::format(
+          "expected out of size {} but was of size {}", size, out.size()));
 }
 
 } // namespace ssl
diff --git a/folly/folly/ssl/OpenSSLHash.h b/folly/folly/ssl/OpenSSLHash.h
--- a/folly/folly/ssl/OpenSSLHash.h
+++ b/folly/folly/ssl/OpenSSLHash.h
@@ -146,9 +146,13 @@
     hash.hash_update(data);
     hash.hash_final(out);
   }
+  [[deprecated(
+      "sha1 should never be used. It is slow and insecure. See https://www.internalfb.com/wiki/What_Hash_Should_I_Use")]]
   static void sha1(MutableByteRange out, ByteRange data) {
     hash(out, EVP_sha1(), data);
   }
+  [[deprecated(
+      "sha1 should never be used. It is slow and insecure. See https://www.internalfb.com/wiki/What_Hash_Should_I_Use")]]
   static void sha1(MutableByteRange out, const IOBuf& data) {
     hash(out, EVP_sha1(), data);
   }
@@ -290,9 +294,13 @@
     hmac.hash_update(data);
     hmac.hash_final(out);
   }
+  [[deprecated(
+      "sha1 should never be used. It is slow and insecure. See https://www.internalfb.com/wiki/What_Hash_Should_I_Use")]]
   static void hmac_sha1(MutableByteRange out, ByteRange key, ByteRange data) {
     hmac(out, EVP_sha1(), key, data);
   }
+  [[deprecated(
+      "sha1 should never be used. It is slow and insecure. See https://www.internalfb.com/wiki/What_Hash_Should_I_Use")]]
   static void hmac_sha1(
       MutableByteRange out, ByteRange key, const IOBuf& data) {
     hmac(out, EVP_sha1(), key, data);
diff --git a/folly/folly/ssl/OpenSSLKeyUtils.cpp b/folly/folly/ssl/OpenSSLKeyUtils.cpp
--- a/folly/folly/ssl/OpenSSLKeyUtils.cpp
+++ b/folly/folly/ssl/OpenSSLKeyUtils.cpp
@@ -78,5 +78,16 @@
   }
   return pkey;
 }
+
+std::string OpenSSLKeyUtils::encodePrivateKeyAsPEM(EVP_PKEY* pkey) {
+  ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));
+  if (!PEM_write_bio_PrivateKey(
+          bio.get(), pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
+    throw std::runtime_error("Failed to encode privatekey");
+  }
+  BUF_MEM* bptr = nullptr;
+  BIO_get_mem_ptr(bio.get(), &bptr);
+  return {bptr->data, bptr->length};
+}
 } // namespace ssl
 } // namespace folly
diff --git a/folly/folly/ssl/OpenSSLKeyUtils.h b/folly/folly/ssl/OpenSSLKeyUtils.h
--- a/folly/folly/ssl/OpenSSLKeyUtils.h
+++ b/folly/folly/ssl/OpenSSLKeyUtils.h
@@ -51,6 +51,14 @@
    */
   static EvpPkeyUniquePtr readPrivateKeyFromBuffer(
       ByteRange range, const PasswordCollector* pwCollector);
+
+  /**
+   * Takes a EVP_PKEY pointer and returns a PEM encoded representation of
+   * that key
+   * @param pkey A EVP_PKEY pointer.
+   * @return PEM encoded string representation of the key.
+   */
+  static std::string encodePrivateKeyAsPEM(EVP_PKEY* pkey);
 };
 } // namespace ssl
 } // namespace folly
diff --git a/folly/folly/stats/BucketedTimeSeries-inl.h b/folly/folly/stats/BucketedTimeSeries-inl.h
--- a/folly/folly/stats/BucketedTimeSeries-inl.h
+++ b/folly/folly/stats/BucketedTimeSeries-inl.h
@@ -19,8 +19,6 @@
 #include <algorithm>
 #include <stdexcept>
 
-#include <glog/logging.h>
-
 #include <folly/Likely.h>
 
 namespace folly {
@@ -121,9 +119,10 @@
     // Current time.
     bucketIdx = getBucketIdx(now);
   } else {
+    firstTime_ = std::min(firstTime_, now);
     // An earlier time in the past.  We need to check if this time still falls
     // within our window.
-    if (now < getEarliestTimeNonEmpty()) {
+    if (now < getEarliestTrackableTimeBy(latestTime_)) {
       return false;
     }
     bucketIdx = getBucketIdx(now);
@@ -139,6 +138,8 @@
   if (empty()) {
     // This is the first data point.
     firstTime_ = now;
+  } else {
+    firstTime_ = std::min(firstTime_, now);
   }
 
   // For all-time data, all we need to do is update latestTime_
@@ -230,7 +231,7 @@
   }
 
   // Compute the earliest time we can track
-  TimePoint earliestTime = getEarliestTimeNonEmpty();
+  TimePoint earliestTime = getEarliestTrackableTimeBy(latestTime_);
 
   // We're never tracking data before firstTime_
   earliestTime = std::max(earliestTime, firstTime_);
@@ -239,13 +240,14 @@
 }
 
 template <typename VT, typename CT>
-typename CT::time_point BucketedTimeSeries<VT, CT>::getEarliestTimeNonEmpty()
-    const {
+typename CT::time_point BucketedTimeSeries<VT, CT>::getEarliestTrackableTimeBy(
+    TimePoint latestTime) const {
+  DCHECK(!isAllTime());
   size_t currentBucket;
   TimePoint currentBucketStart;
   TimePoint nextBucketStart;
   getBucketInfo(
-      latestTime_, &currentBucket, &currentBucketStart, &nextBucketStart);
+      latestTime, &currentBucket, &currentBucketStart, &nextBucketStart);
 
   // Subtract 1 duration from the start of the next bucket to find the
   // earliest possible data point we could be tracking.
@@ -333,6 +335,50 @@
   }
 
   return detail::avgHelper<ReturnType>(total, sample_count);
+}
+
+template <typename VT, typename CT>
+typename BucketedTimeSeries<VT, CT>::Bucket BucketedTimeSeries<VT, CT>::totalBy(
+    TimePoint now) const {
+  DCHECK(now >= latestTime_);
+  if (count() == 0) {
+    // fast-path when there is no samples in the timeseries at all
+    return Bucket{};
+  }
+
+  // Can't be empty if the count is non-zero.
+  DCHECK(!empty());
+
+  if (isAllTime() || now == latestTime_) {
+    return total_;
+  }
+
+  size_t currentBucket;
+  TimePoint currentBucketStart;
+  TimePoint nextBucketStart;
+  getBucketInfo(
+      latestTime_, &currentBucket, &currentBucketStart, &nextBucketStart);
+
+  if (now < nextBucketStart) {
+    // `now` falls in the latest bucket
+    return total_;
+  } else if (now >= currentBucketStart + duration_) {
+    // We do not need to go through the buckets when all of them have expired.
+    return Bucket{};
+  } else {
+    // There is a partial overlap. Subtract tail bucket values from the total.
+    Bucket ret = total_;
+    size_t newBucket = getBucketIdx(now);
+    size_t idx = currentBucket;
+    while (idx != newBucket) {
+      ++idx;
+      if (idx >= buckets_.size()) {
+        idx = 0;
+      }
+      ret -= buckets_[idx];
+    }
+    return ret;
+  }
 }
 
 /*
diff --git a/folly/folly/stats/BucketedTimeSeries.h b/folly/folly/stats/BucketedTimeSeries.h
--- a/folly/folly/stats/BucketedTimeSeries.h
+++ b/folly/folly/stats/BucketedTimeSeries.h
@@ -18,6 +18,7 @@
 
 #include <chrono>
 #include <vector>
+#include <glog/logging.h>
 
 #include <folly/stats/detail/Bucket.h>
 
@@ -144,10 +145,8 @@
   void clear();
 
   /*
-   * Get the latest time that has ever been passed to update() or addValue().
-   *
-   * If no data has ever been added to this timeseries, 0 will be returned.
-   */
+   * See latestTime()
+   **/
   TimePoint getLatestTime() const { return latestTime_; }
 
   /*
@@ -197,14 +196,16 @@
   }
 
   /*
-   * Returns time of first update() since clear()/constructor.
-   * Note that the returned value is only meaningful when empty() is false.
+   * Returns the oldest time observed either by adding value or calling
+   * update() since clear()/constructor. Note that the returned value is only
+   * meaningful when empty() is false.
    */
   TimePoint firstTime() const { return firstTime_; }
 
   /*
-   * Returns time of last update().
-   * Note that the returned value is only meaningful when empty() is false.
+   * Returns the latest time observed either by adding value or calling
+   * update() since clear()/constructor. Note that the returned value is only
+   * meaningful when empty() is false.
    */
   TimePoint latestTime() const { return latestTime_; }
 
@@ -372,6 +373,17 @@
   }
 
   /*
+   * Return the total (sum and count) of the tracked buckets that overlap
+   * with [getEarliestTrackableTimeBy(now), now]. `now` must be greater than or
+   * equal to `latestTime_`. If isAllTime() is true, the overall total will be
+   * returned.
+   *
+   * The user does NOT need to call `update(now)` separately. This is for
+   * providing a way for reading the timeseries without mutating it.
+   */
+  Bucket totalBy(TimePoint now) const;
+
+  /*
    * Invoke a function for each bucket.
    *
    * The function will take as arguments the bucket index,
@@ -437,14 +449,22 @@
   }
   size_t update(Duration now) { return update(TimePoint(now)); }
 
- private:
+  /*
+   * For a non-all-time timeseries, return the earliest time that _could be_
+   * tracked by the timeseries with the provided latest time. It can return a
+   * time point that is earlier than firstTime_.
+   */
+  TimePoint getEarliestTrackableTimeBy(TimePoint latestTime) const;
+
   template <typename ReturnType = double, typename Interval = Duration>
   ReturnType rateHelper(ReturnType numerator, Duration elapsedTime) const {
+    DCHECK(isAllTime() || (Interval{1} <= duration_))
+        << "The timeseries cannot provide a good rate calculation when the interval is larger than the entire duration of the timeseries";
     return detail::rateHelper<ReturnType, Duration, Interval>(
         numerator, elapsedTime);
   }
 
-  TimePoint getEarliestTimeNonEmpty() const;
+ private:
   size_t updateBuckets(TimePoint now);
 
   template <typename ReturnType>
diff --git a/folly/folly/stats/DigestBuilder-inl.h b/folly/folly/stats/DigestBuilder-inl.h
--- a/folly/folly/stats/DigestBuilder-inl.h
+++ b/folly/folly/stats/DigestBuilder-inl.h
@@ -69,16 +69,16 @@
     }
   }
 
-  std::vector<DigestT> digests;
-  digests.reserve(digestPtrs.size());
-  for (auto& digestPtr : digestPtrs) {
-    digests.push_back(std::move(*digestPtr));
-  }
-
   size_t count = 0;
   for (const auto& vec : valuesVec) {
     count += vec.size();
   }
+
+  std::vector<DigestT> digests;
+  digests.reserve(digestPtrs.size() + (count > 0 ? 1 : 0));
+  for (auto& digestPtr : digestPtrs) {
+    digests.push_back(std::move(*digestPtr));
+  }
   if (count) {
     std::vector<double> values;
     values.reserve(count);
@@ -107,6 +107,14 @@
         &cpuLocalBuffers_[AccessSpreader<>::cachedCurrent(numBuffers)];
     g = std::unique_lock(cpuLocalBuf->mutex);
   }
+
+  // CAUTION: This looks like a good opportunity to preallocate the vector. But
+  // the vector is per-quantile per-core, meaning there can be quite a lot of
+  // these vectors, and it is expected that only the hottest of them will ever
+  // actually get full. For those buffers which do actually get full, we already
+  // take care to avoid deallocating below or to preallocate the max in build().
+  // For all other vectors, we need to be conscious of memory usage. So do not
+  // preallocate here.
 
   cpuLocalBuf->buffer.push_back(value);
   if (FOLLY_UNLIKELY(cpuLocalBuf->buffer.size() == bufferSize_)) {
diff --git a/folly/folly/stats/Histogram.h b/folly/folly/stats/Histogram.h
--- a/folly/folly/stats/Histogram.h
+++ b/folly/folly/stats/Histogram.h
@@ -41,8 +41,8 @@
 template <typename T, typename BucketT>
 class HistogramBuckets {
  public:
-  typedef T ValueType;
-  typedef BucketT BucketType;
+  using ValueType = T;
+  using BucketType = BucketT;
 
   /*
    * Create a set of histogram buckets.
@@ -230,8 +230,8 @@
 template <typename T>
 class Histogram {
  public:
-  typedef T ValueType;
-  typedef detail::Bucket<T> Bucket;
+  using ValueType = T;
+  using Bucket = detail::Bucket<T>;
 
   Histogram(ValueType bucketSize, ValueType min, ValueType max)
       : buckets_(bucketSize, min, max, Bucket()) {}
diff --git a/folly/folly/stats/MultiLevelTimeSeries-inl.h b/folly/folly/stats/MultiLevelTimeSeries-inl.h
--- a/folly/folly/stats/MultiLevelTimeSeries-inl.h
+++ b/folly/folly/stats/MultiLevelTimeSeries-inl.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <numeric>
 #include <glog/logging.h>
 
 #include <folly/ConstexprMath.h>
@@ -41,9 +42,48 @@
     prev = dur;
     i++;
   }
+
+  cacheDuration_ = computeMaxCacheDuration();
 }
 
+/*
+ * This is an optimization that allows MultiLevelTimeSeries to cache values
+ * longer on the write path before flushing them to the underlying
+ * BucketedTimeSeries. It preserves all existing semantics of the
+ * timeseries and produces no observable changes to any user if one
+ * follows the API contract correctly (e.g. always call `update()` before
+ * reading the timeseries to get the latest data).
+ * Specifically, if time point T belongs to bucket B, values added at T should
+ * be reflected in bucket B. If readers are calling update() correctly, it
+ * follows that reading of an entire level (BucketedTimeSeries) or an interval
+ * of a level will observe exactly the same value as if every write is flushed
+ * immediately with no caching.
+ */
 template <typename VT, typename CT>
+typename MultiLevelTimeSeries<VT, CT>::Duration
+MultiLevelTimeSeries<VT, CT>::computeMaxCacheDuration() {
+  // Every integer divides 0, so we don't need to special case the first gcd
+  // calculation. We also use it to indicate caching forever.
+  size_t cacheTickCount = 0;
+  for (const auto& level : levels_) {
+    if (level.isAllTime()) {
+      continue;
+    }
+    size_t actualBucketCnt = level.numBuckets();
+    size_t durationTickCnt = level.duration().count();
+    if (durationTickCnt % actualBucketCnt != 0) {
+      // only cache one tick worth of updates when bucket size is a fraction
+      // since the cache duration is counted by the number of ticks
+      return Duration{1};
+    }
+    // gcd is associative
+    cacheTickCount =
+        std::gcd(cacheTickCount, durationTickCnt / actualBucketCnt);
+  }
+  return Duration{cacheTickCount};
+}
+
+template <typename VT, typename CT>
 void MultiLevelTimeSeries<VT, CT>::addValue(
     TimePoint now, const ValueType& val) {
   addValueAggregated(now, val, 1);
@@ -58,10 +98,31 @@
 template <typename VT, typename CT>
 void MultiLevelTimeSeries<VT, CT>::addValueAggregated(
     TimePoint now, const ValueType& total, uint64_t nsamples) {
-  if (cachedTime_ != now) {
+  // if the only level we have is an all-time BucketedTimeseries (have
+  // cacheDuration_ be 0), we will just cache the value forever, and only flush
+  // on read (essentially when update() is called)
+  if (cacheDuration_ != Duration{0} &&
+      (now < cachedTime_ || now >= cachedTime_ + cacheDuration_)) {
     flush();
-    cachedTime_ = now;
+    // cachedTime_ represents the time point of the next flush for all the
+    // cached values. All time points within the cache duration, should belong
+    // to the same bucket as cachedTime_ to preserve the current semantics. This
+    // is achieved by rounding _down_ `now` to align with cacheDuration_
+    // boundaries, so from the BucketedTimeSeries's point of view, clock jumps
+    // forward in multiples of cacheDuration_.
+    cachedTime_ = TimePoint(
+        Duration{
+            (now.time_since_epoch().count() / cacheDuration_.count()) *
+            cacheDuration_.count()});
   }
+
+  for (size_t i = 0; i < levels_.size(); ++i) {
+    if (!levels_[i].isAllTime()) {
+      DCHECK_EQ(
+          levels_[i].getBucketIdx(now), levels_[i].getBucketIdx(cachedTime_));
+    }
+  }
+
   // We have no control over how many different values get added to a time
   // series.
   // We also have no control over their value. We also want to keep some partial
diff --git a/folly/folly/stats/MultiLevelTimeSeries.h b/folly/folly/stats/MultiLevelTimeSeries.h
--- a/folly/folly/stats/MultiLevelTimeSeries.h
+++ b/folly/folly/stats/MultiLevelTimeSeries.h
@@ -25,6 +25,7 @@
 
 #include <glog/logging.h>
 
+#include <folly/ConstexprMath.h>
 #include <folly/String.h>
 #include <folly/stats/BucketedTimeSeries.h>
 
@@ -106,7 +107,7 @@
    * not been called recently.
    */
   const Level& getLevel(size_t level) const {
-    CHECK_LT(level, levels_.size());
+    DCHECK_LT(level, levels_.size());
     return levels_[level];
   }
 
@@ -153,8 +154,9 @@
         return level;
       }
     }
-    throw std::out_of_range(folly::to<std::string>(
-        "No level of duration ", duration.count(), " found"));
+    throw std::out_of_range(
+        folly::to<std::string>(
+            "No level of duration ", duration.count(), " found"));
   }
 
   /*
@@ -167,6 +169,19 @@
   ValueType sum(size_t level) const { return getLevel(level).sum(); }
 
   /*
+   * Return the sum of all the data points currently tracked by this
+   * level, as if `update(now)` is called first. The user
+   * does NOT need to call `update(now)` separately.
+   *
+   * This is for providing a way for reading the timeseries without mutation.
+   */
+  ValueType sumBy(size_t level, TimePoint now) const {
+    auto [total, includeCache] = totalBy(level, now);
+    return constexpr_add_overflow_clamped(
+        total.sum, includeCache ? cachedSum_ : 0);
+  }
+
+  /*
    * Return the average (sum / count) of all the data points currently tracked
    * at this level.
    *
@@ -183,6 +198,23 @@
   }
 
   /*
+   * Return the average (sum/count) of all the data points currently tracked by
+   * this level, as if `update(now)` is called first. The user does NOT need to
+   * call `update(now)` separately.
+   *
+   * This is for providing a way for reading the timeseries without mutation.
+   */
+  template <typename ReturnType = double>
+  ReturnType avgBy(size_t level, TimePoint now) const {
+    auto [total, includeCache] = totalBy(level, now);
+    return folly::detail::avgHelper<ReturnType>(
+        constexpr_add_overflow_clamped(
+            total.sum, includeCache ? cachedSum_ : 0),
+        constexpr_add_overflow_clamped(
+            total.count, includeCache ? cachedCount_ : 0));
+  }
+
+  /*
    * Return the rate (sum divided by elaspsed time) of the all data points
    * currently tracked at this level.
    *
@@ -196,6 +228,20 @@
   }
 
   /*
+   * Return the rate (sum divided by elapsed time) of all the data points
+   * currently tracked by this level, as if `update(now)` is called first. The
+   * user does NOT need to call `update(now)` separately.
+   *
+   * This is for providing a way for reading the timeseries without mutation.
+   */
+  template <typename ReturnType = double, typename Interval = Duration>
+  ReturnType rateBy(size_t level, TimePoint now) const {
+    const auto& timeseries = getLevel(level);
+    return timeseries.template rateHelper<ReturnType, Interval>(
+        sumBy(level, now), elapsedBy(level, now));
+  }
+
+  /*
    * Return the number of data points currently tracked at this level.
    *
    * Note: you should generally call update() or flush() before accessing the
@@ -205,6 +251,19 @@
   uint64_t count(size_t level) const { return getLevel(level).count(); }
 
   /*
+   * Return the count of all the data points currently tracked by this
+   * level, as if `update(now)` is called first. The user
+   * does NOT need to call `update(now)` separately.
+   *
+   * This is for providing a way for reading the timeseries without mutation.
+   */
+  uint64_t countBy(size_t level, TimePoint now) const {
+    auto [total, includeCache] = totalBy(level, now);
+    return constexpr_add_overflow_clamped(
+        total.count, includeCache ? cachedCount_ : 0);
+  }
+
+  /*
    * Return the count divided by the elapsed time tracked at this level.
    *
    * Note: you should generally call update() or flush() before accessing the
@@ -217,6 +276,20 @@
   }
 
   /*
+   * Return the count divided by elapsed time of all the data points currently
+   * tracked by this level, as if `update(now)` is called first. The user does
+   * NOT need to call `update(now)` separately.
+   *
+   * This is for providing a way for reading the timeseries without mutation.
+   */
+  template <typename ReturnType = double, typename Interval = Duration>
+  ReturnType countRateBy(size_t level, TimePoint now) const {
+    const auto& timeseries = getLevel(level);
+    return timeseries.template rateHelper<ReturnType, Interval>(
+        countBy(level, now), elapsedBy(level, now));
+  }
+
+  /*
    * Return the sum of all the data points currently tracked at this level.
    *
    * This method is identical to sum(size_t level) above but takes in the
@@ -339,9 +412,9 @@
    * data. Otherwise you may be reading stale data if update() or flush() has
    * not been called recently.
    */
-  template <typename ReturnType = double>
+  template <typename ReturnType = double, typename Interval = Duration>
   ReturnType rate(TimePoint start, TimePoint end) const {
-    return getLevel(start).template rate<ReturnType>(start, end);
+    return getLevel(start).template rate<ReturnType, Interval>(start, end);
   }
 
   /*
@@ -426,6 +499,55 @@
   }
 
  private:
+  Duration computeMaxCacheDuration();
+
+  Duration elapsedBy(size_t level, TimePoint now) const {
+    const auto& timeseries = getLevel(level);
+    TimePoint firstTime = now;
+    if (cachedCount_ != 0) {
+      firstTime = std::min(firstTime, cachedTime_);
+    }
+    if (!timeseries.empty()) {
+      firstTime = std::min(firstTime, timeseries.firstTime());
+    }
+    auto latestTime = std::max({now, cachedTime_, timeseries.getLatestTime()});
+    if (timeseries.isAllTime()) {
+      return latestTime - firstTime + Duration(1);
+    } else {
+      return latestTime -
+          std::max(
+                 firstTime, timeseries.getEarliestTrackableTimeBy(latestTime)) +
+          Duration(1);
+    }
+  }
+
+  std::pair<typename Level::Bucket, bool> totalBy(
+      size_t level, TimePoint now) const {
+    const auto& timeseries = getLevel(level);
+    if (cachedCount_ == 0) {
+      return {
+          timeseries.totalBy(std::max(now, timeseries.getLatestTime())), false};
+    }
+
+    // Regardless if cache value is still captured or not, we still need to
+    // rotate the buckets to the max timestamps.
+    auto latestTime = std::max({timeseries.getLatestTime(), now, cachedTime_});
+    if (timeseries.isAllTime()) {
+      return {timeseries.totalBy(latestTime), true};
+    }
+
+    // In order to decide if we should include cached value in the result or
+    // not, all we need to know is if cachedTime_ is still within the trackable
+    // window.
+    auto earliestTime = timeseries.getEarliestTrackableTimeBy(latestTime);
+    if (cachedTime_ < earliestTime) {
+      // Cached value is too old, and not tracked by the timeseries any more.
+      return {timeseries.totalBy(latestTime), false};
+    } else {
+      return {timeseries.totalBy(latestTime), true};
+    }
+  }
+
   std::vector<Level> levels_;
 
   // Updates within the same time interval are cached
@@ -434,6 +556,7 @@
   TimePoint cachedTime_;
   ValueType cachedSum_;
   uint64_t cachedCount_;
+  Duration cacheDuration_;
 };
 
 } // namespace folly
diff --git a/folly/folly/stats/QuantileEstimator.cpp b/folly/folly/stats/QuantileEstimator.cpp
--- a/folly/folly/stats/QuantileEstimator.cpp
+++ b/folly/folly/stats/QuantileEstimator.cpp
@@ -26,8 +26,7 @@
   result.sum = digest.sum();
   result.count = digest.count();
   for (auto& quantile : quantiles) {
-    result.quantiles.push_back(
-        std::make_pair(quantile, digest.estimateQuantile(quantile)));
+    result.quantiles.emplace_back(quantile, digest.estimateQuantile(quantile));
   }
   return result;
 }
diff --git a/folly/folly/stats/TimeseriesHistogram.h b/folly/folly/stats/TimeseriesHistogram.h
--- a/folly/folly/stats/TimeseriesHistogram.h
+++ b/folly/folly/stats/TimeseriesHistogram.h
@@ -309,7 +309,7 @@
   }
 
  private:
-  typedef ContainerType Bucket;
+  using Bucket = ContainerType;
   struct CountFromLevel {
     explicit CountFromLevel(size_t level) : level_(level) {}
 
diff --git a/folly/folly/stats/detail/Bucket.h b/folly/folly/stats/detail/Bucket.h
--- a/folly/folly/stats/detail/Bucket.h
+++ b/folly/folly/stats/detail/Bucket.h
@@ -107,36 +107,42 @@
 template <
     typename ReturnType = double,
     typename Duration = std::chrono::seconds,
-    typename Interval = Duration>
+    typename Interval = Duration,
+    typename = std::enable_if_t<std::is_constructible_v<Duration, Interval>>>
 ReturnType rateHelper(ReturnType count, Duration elapsed) {
   if (elapsed == Duration(0)) {
     return 0;
   }
 
+  // elapsed is non-zero, and we increase elapsed to at least Interval{1}
+  // for rate calculation to smooth out rate calculation at the beginning for
+  // a timeseries' lifecycle, when elapsed is very short compared to the overall
+  // timeseries duration.
+  elapsed = std::max(elapsed, Duration(Interval{1}));
+
   // Use std::chrono::duration_cast to convert between the native
   // duration and the desired interval.  However, convert the rates,
   // rather than just converting the elapsed duration.  Converting the
   // elapsed time first may collapse it down to 0 if the elapsed interval
   // is less than the desired interval, which will incorrectly result in
   // an infinite rate.
-  typedef std::chrono::duration<
-      ReturnType,
-      std::ratio<Duration::period::den, Duration::period::num>>
-      NativeRate;
-  typedef std::chrono::duration<
-      ReturnType,
-      std::ratio<Interval::period::den, Interval::period::num>>
-      DesiredRate;
+  using NativeRate = std::chrono::duration<
+      double,
+      std::ratio<Duration::period::den, Duration::period::num>>;
+  using DesiredRate = std::chrono::duration<
+      double,
+      std::ratio<Interval::period::den, Interval::period::num>>;
 
-  NativeRate native(count / elapsed.count());
+  // We use Rep=double to avoid rouding down too much here.
+  NativeRate native((double)count / elapsed.count());
   DesiredRate desired = std::chrono::duration_cast<DesiredRate>(native);
-  return desired.count();
+  return static_cast<ReturnType>(desired.count());
 }
 
 template <typename T>
 struct Bucket {
  public:
-  typedef T ValueType;
+  using ValueType = T;
 
   Bucket() : sum(ValueType()), count(0) {}
 
diff --git a/folly/folly/synchronization/AsymmetricThreadFence.h b/folly/folly/synchronization/AsymmetricThreadFence.h
--- a/folly/folly/synchronization/AsymmetricThreadFence.h
+++ b/folly/folly/synchronization/AsymmetricThreadFence.h
@@ -24,6 +24,16 @@
 
 //  asymmetric_thread_fence_light
 //
+//  A lightweight fence interacts with a heavyweight fence as if both fences
+//  were normal std::atomic_thread_fence fences.
+//
+//  A lightweight fence does not interact with another lightweight fence, with a
+//  normal fence, with a signal fence, or directly with an atomic operation in
+//  another thread.
+//
+//  https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence.html
+//  https://en.cppreference.com/w/cpp/atomic/atomic_signal_fence.html
+//
 //  mimic: std::experimental::asymmetric_thread_fence_light, p1202r4
 //  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1202r4.pdf
 struct asymmetric_thread_fence_light_fn {
@@ -40,6 +50,16 @@
 
 //  asymmetric_thread_fence_heavy
 //
+//  A heavyweight fence interacts with a heavyweight, lightweight, or normal
+//  fence as if both fences were normal std::atomic_thread_fence fences, and
+//  interacts directly with an atomic operation as if it were a normal fence.
+//
+//  A heavyweight fence interacts with a signal fence as if both fences were
+//  signal fences, just as a normal fence would do.
+//
+//  https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence.html
+//  https://en.cppreference.com/w/cpp/atomic/atomic_signal_fence.html
+//
 //  mimic: std::experimental::asymmetric_thread_fence_heavy, p1202r4
 //  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1202r4.pdf
 struct asymmetric_thread_fence_heavy_fn {
@@ -56,5 +76,14 @@
 };
 inline constexpr asymmetric_thread_fence_heavy_fn
     asymmetric_thread_fence_heavy{};
+
+template <template <typename> class Atom>
+struct asymmetric_thread_fence_traits;
+
+template <>
+struct asymmetric_thread_fence_traits<std::atomic> {
+  static inline constexpr asymmetric_thread_fence_light_fn light{};
+  static inline constexpr asymmetric_thread_fence_heavy_fn heavy{};
+};
 
 } // namespace folly
diff --git a/folly/folly/synchronization/AtomicNotification-inl.h b/folly/folly/synchronization/AtomicNotification-inl.h
--- a/folly/folly/synchronization/AtomicNotification-inl.h
+++ b/folly/folly/synchronization/AtomicNotification-inl.h
@@ -61,8 +61,7 @@
 }
 
 template <
-    template <typename...>
-    class Atom,
+    template <typename...> class Atom,
     typename... Args,
     typename Clock,
     typename Duration>
@@ -74,8 +73,7 @@
 }
 
 template <
-    template <typename...>
-    class Atom,
+    template <typename...> class Atom,
     typename Integer,
     typename... Args,
     typename Clock,
diff --git a/folly/folly/synchronization/AtomicUtil.h b/folly/folly/synchronization/AtomicUtil.h
--- a/folly/folly/synchronization/AtomicUtil.h
+++ b/folly/folly/synchronization/AtomicUtil.h
@@ -182,9 +182,17 @@
   atomic_value_type_t<Atomic> operator()(
       Atomic& atomic,
       Op op,
-      std::memory_order = std::memory_order_seq_cst) const;
+      std::memory_order mo = std::memory_order_seq_cst) const;
 };
 inline constexpr atomic_fetch_modify_fn atomic_fetch_modify{};
+
+template <template <typename> class Atom>
+struct atomic_thread_fence_traits;
+
+template <>
+struct atomic_thread_fence_traits<std::atomic> {
+  static inline constexpr auto fence = std::atomic_thread_fence;
+};
 
 } // namespace folly
 
diff --git a/folly/folly/synchronization/Baton.h b/folly/folly/synchronization/Baton.h
--- a/folly/folly/synchronization/Baton.h
+++ b/folly/folly/synchronization/Baton.h
@@ -31,6 +31,7 @@
 #include <folly/detail/AsyncTrace.h>
 #include <folly/detail/Futex.h>
 #include <folly/detail/MemoryIdler.h>
+#include <folly/lang/Switch.h>
 #include <folly/portability/Asm.h>
 #include <folly/synchronization/AtomicUtil.h>
 #include <folly/synchronization/WaitOptions.h>
@@ -314,26 +315,36 @@
               deadline - Clock::now()));
     }
 
-    switch (detail::spin_pause_until(deadline, opt, [this] {
-      return ready();
-    })) {
-      case detail::spin_result::success:
-        return true;
-      case detail::spin_result::timeout:
-        return false;
-      case detail::spin_result::advance:
-        break;
-    }
-
-    if (!MayBlock) {
-      switch (detail::spin_yield_until(deadline, [this] { return ready(); })) {
+    FOLLY_EXHAUSTIVE_SWITCH({
+      switch (detail::spin_pause_until(deadline, opt, [this] {
+        return ready();
+      })) {
         case detail::spin_result::success:
           return true;
         case detail::spin_result::timeout:
           return false;
         case detail::spin_result::advance:
           break;
+        default:
+          folly::assume_unreachable();
       }
+    });
+
+    if (!MayBlock) {
+      FOLLY_EXHAUSTIVE_SWITCH({
+        switch (detail::spin_yield_until(deadline, [this] {
+          return ready();
+        })) {
+          case detail::spin_result::success:
+            return true;
+          case detail::spin_result::timeout:
+            return false;
+          case detail::spin_result::advance:
+            break;
+          default:
+            folly::assume_unreachable();
+        }
+      });
     }
 
     // Try transitioning from the spinning phase to the blocking phase via a CAS
diff --git a/folly/folly/synchronization/CallOnce.h b/folly/folly/synchronization/CallOnce.h
--- a/folly/folly/synchronization/CallOnce.h
+++ b/folly/folly/synchronization/CallOnce.h
@@ -84,7 +84,7 @@
  * Note: This has no parallel in the std::once_flag interface.
  */
 template <typename OnceFlag, typename F, typename... Args>
-FOLLY_NODISCARD FOLLY_ALWAYS_INLINE bool try_call_once(
+[[nodiscard]] FOLLY_ALWAYS_INLINE bool try_call_once(
     OnceFlag& flag, F&& f, Args&&... args) noexcept {
   static_assert(is_nothrow_invocable_v<F, Args...>, "must be noexcept");
   if (FOLLY_LIKELY(flag.test_once())) {
diff --git a/folly/folly/synchronization/DistributedMutex-inl.h b/folly/folly/synchronization/DistributedMutex-inl.h
--- a/folly/folly/synchronization/DistributedMutex-inl.h
+++ b/folly/folly/synchronization/DistributedMutex-inl.h
@@ -1147,8 +1147,7 @@
 }
 
 template <
-    template <typename>
-    class Atomic,
+    template <typename> class Atomic,
     bool TimePublishing,
     typename State,
     typename Request>
diff --git a/folly/folly/synchronization/DistributedMutex.h b/folly/folly/synchronization/DistributedMutex.h
--- a/folly/folly/synchronization/DistributedMutex.h
+++ b/folly/folly/synchronization/DistributedMutex.h
@@ -49,9 +49,9 @@
  * faster than flat combining and even faster than std::atomic<> in some
  * cases, allowing more work with higher throughput.  In the uncontended case,
  * it is a few cycles faster than folly::MicroLock but a bit slower than
- * std::mutex.  DistributedMutex is also resistent to tail latency pathalogies
+ * std::mutex.  DistributedMutex is also resistant to tail latency pathologies
  * unlike many of the other mutexes in use, which sleep for large time
- * quantums to reduce spin churn, this causes elevated latencies for threads
+ * quantum to reduce spin churn, this causes elevated latencies for threads
  * that enter the sleep cycle.  The tail latency of lock acquisition can go up
  * to 10x lower because of a more deterministic scheduling algorithm that is
  * managed almost entirely in userspace.  Detailed results comparing the
@@ -299,9 +299,9 @@
   auto lock_combine(Task task) -> folly::invoke_result_t<const Task&>;
 
   /**
-   * Try to combine a task as a combined critical section untill the given time
+   * Try to combine a task as a combined critical section until the given time
    *
-   * Like the other try_lock() mehtods, this is allowed to fail spuriously,
+   * Like the other try_lock() methods, this is allowed to fail spuriously,
    * and is not guaranteed to return true even when the mutex is currently
    * unlocked.
    *
@@ -314,7 +314,7 @@
       const std::chrono::duration<Rep, Period>& duration, Task task);
 
   /**
-   * Try to combine a task as a combined critical section untill the given time
+   * Try to combine a task as a combined critical section until the given time
    *
    * Other than the difference in the meaning of the second argument, the
    * semantics of this function are identical to try_lock_combine_for()
diff --git a/folly/folly/synchronization/FlatCombining.h b/folly/folly/synchronization/FlatCombining.h
--- a/folly/folly/synchronization/FlatCombining.h
+++ b/folly/folly/synchronization/FlatCombining.h
@@ -239,6 +239,11 @@
     }
   }
 
+  FlatCombining(const FlatCombining&) = delete;
+  FlatCombining& operator=(const FlatCombining&) = delete;
+  FlatCombining(FlatCombining&&) = delete;
+  FlatCombining& operator=(FlatCombining&&) = delete;
+
   // Wait for all pending operations to complete. Useful primarily
   // when there are asynchronous operations without a dedicated
   // combiner.
diff --git a/folly/folly/synchronization/Hazptr-fwd.h b/folly/folly/synchronization/Hazptr-fwd.h
--- a/folly/folly/synchronization/Hazptr-fwd.h
+++ b/folly/folly/synchronization/Hazptr-fwd.h
@@ -19,6 +19,7 @@
 #include <atomic>
 #include <memory>
 
+#include <folly/Portability.h>
 #include <folly/portability/Config.h>
 
 ///
@@ -34,6 +35,19 @@
 #endif
 
 namespace folly {
+
+namespace detail {
+
+/// hazptr_prefer_fence_light
+///
+/// For select cases of load-acquire and store-release internal to the hazptr
+/// implementation, but occurring within hot paths, whether to prefer relaxed
+/// load and store operations combined with lightweight fences - at the cost of
+/// heavyweight fences in the reclamation path.
+constexpr inline bool hazptr_prefer_fence_light =
+    kIsArchAArch64 && kIsLinux && !kIsSanitizeThread;
+
+} // namespace detail
 
 ///
 /// Hazard pointer record.
diff --git a/folly/folly/synchronization/Hazptr.h b/folly/folly/synchronization/Hazptr.h
--- a/folly/folly/synchronization/Hazptr.h
+++ b/folly/folly/synchronization/Hazptr.h
@@ -194,7 +194,7 @@
 /// --------------------------------------
 /// - The latest standard proposal is in wg21.link/p1121. The
 ///   substance of the proposal was frozen around October 2017, but
-///   there were subsequent API changes based on committee feadback.
+///   there were subsequent API changes based on committee feedback.
 /// - The main differences are:
 ///   o This library uses an extra atomic template parameter for
 ///     testing and debugging.
diff --git a/folly/folly/synchronization/HazptrDomain.cpp b/folly/folly/synchronization/HazptrDomain.cpp
--- a/folly/folly/synchronization/HazptrDomain.cpp
+++ b/folly/folly/synchronization/HazptrDomain.cpp
@@ -17,52 +17,40 @@
 #include <folly/synchronization/HazptrDomain.h>
 
 #include <cstddef>
-#include <functional>
 #include <queue>
 #include <type_traits>
 
-#include <folly/Executor.h>
-#include <folly/Indestructible.h>
+#include <glog/logging.h>
+#include <folly/ExceptionString.h>
+#include <folly/Function.h>
 #include <folly/ScopeGuard.h>
-#include <folly/executors/InlineExecutor.h>
+#include <folly/lang/Exception.h>
 
 namespace folly::detail {
 
-namespace {
-
-/// HazptrDefaultExecutor
-///
-/// Like QueuedImmediateExecutor, but:
-/// * Only a singleton and not otherwise instantiated.
-/// * Using keyword thread_local v.s. class ThreadLocal.
-struct HazptrDefaultExecutor final : InlineLikeExecutor {
-  void add(Func func) override {
-    using Queue = std::queue<Func>;
-    thread_local Queue* current = nullptr;
+void hazptr_inline_executor_add(folly::Function<void()> func) {
+  using Queue = std::queue<folly::Function<void()>>;
+  thread_local Queue* current = nullptr;
 
-    if (current != nullptr) {
-      current->push(std::move(func));
-      return;
-    }
+  if (current != nullptr) {
+    current->push(std::move(func));
+    return;
+  }
 
-    Queue queue;
-    current = &queue;
-    auto cleanup = makeGuard([&] { current = nullptr; });
+  Queue queue;
+  current = &queue;
+  auto cleanup = makeGuard([&] { current = nullptr; });
+  auto logException = []() noexcept {
+    LOG(ERROR) << "HazptrDomain threw unhandled "
+               << exceptionStr(current_exception());
+  };
 
-    invokeCatchingExns("HazptrDefaultExecutor", std::exchange(func, {}));
+  catch_exception(std::exchange(func, {}), logException);
 
-    while (!queue.empty()) {
-      invokeCatchingExns("HazptrDefaultExecutor", std::ref(queue.front()));
-      queue.pop();
-    }
+  while (!queue.empty()) {
+    catch_exception(std::ref(queue.front()), logException);
+    queue.pop();
   }
-};
-
-} // namespace
-
-folly::Executor::KeepAlive<> hazptr_get_default_executor() {
-  static Indestructible<HazptrDefaultExecutor> instance;
-  return &*instance;
 }
 
 } // namespace folly::detail
diff --git a/folly/folly/synchronization/HazptrDomain.h b/folly/folly/synchronization/HazptrDomain.h
--- a/folly/folly/synchronization/HazptrDomain.h
+++ b/folly/folly/synchronization/HazptrDomain.h
@@ -18,11 +18,13 @@
 
 #include <atomic>
 
-#include <folly/Executor.h>
-#include <folly/Memory.h>
+#include <folly/Function.h>
 #include <folly/Portability.h>
+#include <folly/concurrency/container/atomic_grow_array.h>
 #include <folly/container/F14Set.h>
+#include <folly/detail/StaticSingletonManager.h>
 #include <folly/synchronization/AsymmetricThreadFence.h>
+#include <folly/synchronization/AtomicUtil.h>
 #include <folly/synchronization/Hazptr-fwd.h>
 #include <folly/synchronization/HazptrObj.h>
 #include <folly/synchronization/HazptrRec.h>
@@ -34,8 +36,6 @@
 
 namespace folly {
 
-class Executor;
-
 namespace detail {
 
 /** Threshold for the number of retired objects to trigger
@@ -44,8 +44,11 @@
   return 1000;
 }
 
-folly::Executor::KeepAlive<> hazptr_get_default_executor();
+void hazptr_inline_executor_add(folly::Function<void()> func);
 
+template <template <typename> class Atom>
+struct default_hazptr_domain_impl;
+
 } // namespace detail
 
 /**
@@ -104,8 +107,17 @@
   using ObjList = hazptr_obj_list<Atom>;
   using RetiredList = hazptr_detail::shared_head_only_list<Obj, Atom>;
   using Set = folly::F14FastSet<const void*>;
-  using ExecFn = folly::Executor::KeepAlive<> (*)();
+  using Func = folly::Function<void()>;
+  using ExecutorFn = bool (*)(Func&&);
 
+  struct HazptrRecArrayPolicy : atomic_grow_array_policy_default<Rec, Atom> {
+    hazptr_domain* domain_{};
+    /* implicit */ HazptrRecArrayPolicy(hazptr_domain* domain) noexcept
+        : domain_{domain} {}
+    Rec make() const noexcept { return Rec{domain_}; }
+  };
+  using HazptrRecArray = atomic_grow_array<Rec, HazptrRecArrayPolicy>;
+
   static constexpr int kThreshold = detail::hazptr_domain_rcount_threshold();
   static constexpr int kMultiplier = 2;
   static constexpr int kListTooLarge = 100000;
@@ -119,7 +131,7 @@
   static_assert(
       (kNumShards & kShardMask) == 0, "kNumShards must be a power of 2");
 
-  Atom<Rec*> hazptrs_{nullptr};
+  Atom<HazptrRecArray*> hprecs_{nullptr};
   Atom<uintptr_t> avail_{reinterpret_cast<uintptr_t>(nullptr)};
   Atom<uint64_t> sync_time_{0};
   /* Using signed int for rcount_ because it may transiently be negative.
@@ -132,9 +144,14 @@
   RetiredList tagged_[kNumShards];
   Atom<int> count_{0};
   Atom<uint64_t> due_time_{0};
-  Atom<ExecFn> exec_fn_{nullptr};
+  Atom<ExecutorFn> exec_fn_{&no_executor};
   Atom<int> exec_backlog_{0};
 
+  /** is_default_domain_ is loaded in every call to make_hazard_pointer and
+   *  ~hazptr_holder. This is the only piece of domain state that is loaded for
+   *  reader chrome or critical sections when using the default domain. */
+  alignas(hardware_destructive_interference_size) bool is_default_domain_{};
+
  public:
   /** Constructor */
   hazptr_domain() = default;
@@ -156,11 +173,15 @@
   hazptr_domain& operator=(const hazptr_domain&) = delete;
   hazptr_domain& operator=(hazptr_domain&&) = delete;
 
-  void set_executor(ExecFn exfn) {
+  bool is_default_domain() const noexcept { return is_default_domain_; }
+
+  void set_executor(ExecutorFn exfn) {
     exec_fn_.store(exfn, std::memory_order_release);
   }
 
-  void clear_executor() { exec_fn_.store(nullptr, std::memory_order_release); }
+  void clear_executor() {
+    exec_fn_.store(&no_executor, std::memory_order_release);
+  }
 
   /** retire - nonintrusive - allocates memory */
   template <typename T, typename D = std::default_delete<T>>
@@ -191,14 +212,7 @@
     // Call cleanup() to ensure that there is no lagging concurrent
     // asynchronous reclamation in progress.
     cleanup();
-    Rec* rec = head();
-    while (rec) {
-      auto next = rec->next();
-      rec->~Rec();
-      hazptr_rec_alloc{}.deallocate(rec, 1);
-      rec = next;
-    }
-    hazptrs_.store(nullptr);
+    delete hprecs_.exchange(nullptr);
     hcount_.store(0);
     avail_.store(reinterpret_cast<uintptr_t>(nullptr));
   }
@@ -229,8 +243,7 @@
   }
 
  private:
-  using hazptr_rec_alloc = AlignedSysAllocator<Rec, FixedAlign<alignof(Rec)>>;
-
+  friend struct detail::default_hazptr_domain_impl<Atom>;
   friend void hazptr_domain_push_retired<Atom>(
       hazptr_obj_list<Atom>&, hazptr_domain<Atom>&) noexcept;
   friend hazptr_holder<Atom> make_hazard_pointer<Atom>(hazptr_domain<Atom>&);
@@ -243,6 +256,13 @@
   friend class hazptr_tc<Atom>;
 #endif
 
+  struct default_domain_tag {};
+
+  static bool no_executor(Func&&) { return false; }
+
+  explicit hazptr_domain(default_domain_tag) noexcept
+      : is_default_domain_{true} {}
+
   int load_count() { return count_.load(std::memory_order_acquire); }
 
   void add_count(int val) { count_.fetch_add(val, std::memory_order_release); }
@@ -298,10 +318,7 @@
   /** acquire_hprecs */
   Rec* acquire_hprecs(uint8_t num) {
     DCHECK_GE(num, 1);
-    // C++17: auto [n, head] = try_pop_available_hprecs(num);
-    uint8_t n;
-    Rec* head;
-    std::tie(n, head) = try_pop_available_hprecs(num);
+    auto [n, head] = try_pop_available_hprecs(num);
     for (; n < num; ++n) {
       Rec* rec = create_new_hprec();
       DCHECK(rec->next_avail() == nullptr);
@@ -349,7 +366,8 @@
   /** threshold */
   int threshold() {
     auto thresh = kThreshold;
-    return std::max(thresh, kMultiplier * hcount());
+    auto hcount = hcount_.load(std::memory_order_relaxed);
+    return std::max(thresh, kMultiplier * hcount);
   }
 
   /** check_threshold_and_reclaim */
@@ -357,13 +375,12 @@
     int rcount = check_count_threshold();
     if (rcount == 0) {
       rcount = check_due_time();
-      if (rcount == 0)
+      if (rcount == 0) {
         return;
+      }
     }
     inc_num_bulk_reclaims();
-    if (!invoke_reclamation_in_executor(rcount)) {
-      do_reclamation(rcount);
-    }
+    schedule_reclamation(rcount);
   }
 
   /** calc_shard */
@@ -385,8 +402,9 @@
             std::chrono::steady_clock::now().time_since_epoch())
             .count();
     auto due = load_due_time();
-    if (time < due || !cas_due_time(due, time + kSyncTimePeriod))
+    if (time < due || !cas_due_time(due, time + kSyncTimePeriod)) {
       return 0;
+    }
     int rcount = exchange_count(0);
     if (rcount < 0) {
       add_count(rcount);
@@ -410,8 +428,9 @@
   /** tagged_empty */
   bool tagged_empty() {
     for (int s = 0; s < kNumShards; ++s) {
-      if (!tagged_[s].empty())
+      if (!tagged_[s].empty()) {
         return false;
+      }
     }
     return true;
   }
@@ -419,8 +438,9 @@
   /** untagged_empty */
   bool untagged_empty() {
     for (int s = 0; s < kNumShards; ++s) {
-      if (!untagged_[s].empty())
+      if (!untagged_[s].empty()) {
         return false;
+      }
     }
     return true;
   }
@@ -457,34 +477,72 @@
   /** load_hazptr_vals */
   Set load_hazptr_vals() {
     Set hs;
-    auto hprec = hazptrs_.load(std::memory_order_acquire);
-    for (; hprec; hprec = hprec->next()) {
-      hs.insert(hprec->hazptr());
+    auto sz = std::max(0, hcount_.load(std::memory_order_relaxed));
+    if (auto* hprecs = hprecs_.load(std::memory_order_acquire)) {
+      // chunk the loop to avoid a single loop-carried dependency on the loop
+      // counter; helpful when the hprecs array is large but sparse
+      constexpr size_t chunk_width = kNumShards;
+      constexpr auto order = kIsSanitizeThread
+          ? std::memory_order_acquire // tsan does not instrument fences
+          : std::memory_order_relaxed; // fence below provides acquire order
+      auto ptrspan = hprecs->as_ptr_span(size_t(sz));
+      for (size_t i = 0; i + chunk_width <= ptrspan.size(); i += chunk_width) {
+        // load a batch of hazard pointers up-front so that the branches below
+        // can run in parallel with each other on x86
+        const void* ptrs[chunk_width];
+        for (size_t j = 0; j < chunk_width; ++j) {
+          auto hprec = ptrspan[i + j];
+          ptrs[j] = hprec->hazptr(order);
+        }
+        // when the hprecs array is sparse, the branches in this loop can run in
+        // parallel with each other on x86 since they are not blocked on loads
+        // from memory or cache; the loads are served either from registers or
+        // from the store buffer and do not block each other
+        for (auto ptr : ptrs) {
+          if (ptr) {
+            hs.insert(ptr);
+          }
+        }
+      }
+      // final undersized chunk; keep it here, rather than blending into the
+      // chunked loop above, to minimize the number of instructions executed in
+      // the main loop body
+      ptrspan = ptrspan.subspan(ptrspan.size() & ~(chunk_width - 1));
+      for (auto hprec : ptrspan) {
+        if (auto ptr = hprec->hazptr(order)) {
+          hs.insert(ptr);
+        }
+      }
+      if constexpr (detail::hazptr_prefer_fence_light) {
+        asymmetric_thread_fence_traits<Atom>::heavy(std::memory_order_acquire);
+      } else {
+        atomic_thread_fence_traits<Atom>::fence(std::memory_order_acquire);
+      }
     }
     return hs;
   }
 
   /** match_tagged */
   int match_tagged(Obj* tagged[], Set& hs) {
+    int counts[kNumShards] = {}; // avoid single loop-carried dependency
+    ObjList match[kNumShards]; // protected, not reclaimable, to be reinserted
+    list_walk_sharded(tagged, [&](int s, Obj* obj) {
+      if (hs.contains(obj->raw_ptr())) {
+        match[s].push(obj);
+      } else {
+        counts[s] += 1;
+        auto cohort = obj->cohort();
+        DCHECK(cohort);
+        cohort->push_safe_obj(obj);
+      }
+    });
     int count = 0;
     for (int s = 0; s < kNumShards; ++s) {
       if (tagged[s]) {
-        ObjList match, nomatch;
-        list_match_condition(tagged[s], match, nomatch, [&](Obj* o) {
-          return hs.count(o->raw_ptr()) > 0;
-        });
-        count += nomatch.count();
-        auto obj = nomatch.head();
-        while (obj) {
-          auto next = obj->next();
-          auto cohort = obj->cohort();
-          DCHECK(cohort);
-          cohort->push_safe_obj(obj);
-          obj = next;
-        }
-        List l(match.head(), match.tail());
+        List l(match[s].head(), match[s].tail());
         tagged_[s].push_unlock(l);
       }
+      count += counts[s];
     }
     return count;
   }
@@ -492,25 +550,30 @@
   /** match_reclaim_untagged */
   int match_reclaim_untagged(Obj* untagged[], Set& hs, bool& done) {
     done = true;
-    ObjList not_reclaimed;
+    int counts[kNumShards] = {}; // avoid single loop-carried dependency
+    ObjList not_reclaimed[kNumShards]; // not reclaimed, to be reinserted
+    list_walk_sharded(untagged, [&](int s, Obj* obj) {
+      if (hs.contains(obj->raw_ptr())) {
+        not_reclaimed[s].push(obj);
+      } else {
+        counts[s] += 1;
+        ObjList children;
+        (*(obj->reclaim()))(obj, children);
+        if (!children.empty()) {
+          done = false;
+          not_reclaimed[s].splice(children);
+        }
+      }
+    });
+    if (!untagged_empty()) {
+      done = false;
+    }
     int count = 0;
     for (int s = 0; s < kNumShards; ++s) {
-      ObjList match, nomatch;
-      list_match_condition(untagged[s], match, nomatch, [&](Obj* o) {
-        return hs.count(o->raw_ptr()) > 0;
-      });
-      ObjList children;
-      count += nomatch.count();
-      reclaim_unprotected(nomatch.head(), children);
-      if (!untagged_empty() || !children.empty()) {
-        done = false;
-      }
-      count -= children.count();
-      not_reclaimed.splice(match);
-      not_reclaimed.splice(children);
+      List l(not_reclaimed[s].head(), not_reclaimed[s].tail());
+      untagged_[s].push(l, RetiredList::kMayNotBeLocked);
+      count += counts[s];
     }
-    List l(not_reclaimed.head(), not_reclaimed.tail());
-    untagged_[0].push(l, RetiredList::kMayNotBeLocked);
     return count;
   }
 
@@ -532,12 +595,38 @@
         add_count(rcount);
       }
       rcount = check_count_threshold();
-      if (rcount == 0 && done)
+      if (rcount == 0 && done) {
         break;
+      }
     }
     dec_num_bulk_reclaims();
   }
 
+  /** list_walk_sharded */
+  template <typename Body>
+  void list_walk_sharded(Obj* objs[kNumShards], Body body) {
+    // Walk shards of a linked list in parallel. A long linked-list can create
+    // long loop-carried dependency chains, eventually stalling the processor.
+    // If a long linked-list is sharded, walking the shards in parallel can help
+    // reduce the impact of loop-carried dependencies. Note that the parallelism
+    // here refers to instruction-level parallism v.s. thread-level parallelism
+    // or processor-level parallelism.
+    Obj* heads[kNumShards];
+    for (int s = 0; s < kNumShards; ++s) {
+      heads[s] = objs[s];
+    }
+    for (bool more = true; more;) {
+      more = false;
+      for (int s = 0; s < kNumShards; ++s) {
+        if (auto obj = heads[s]) {
+          more = true;
+          heads[s] = obj->next();
+          body(s, obj);
+        }
+      }
+    }
+  }
+
   /** list_match_condition */
   template <typename Cond>
   void list_match_condition(
@@ -545,20 +634,8 @@
     while (obj) {
       auto next = obj->next();
       DCHECK_NE(obj, next);
-      if (cond(obj)) {
-        match.push(obj);
-      } else {
-        nomatch.push(obj);
-      }
-      obj = next;
-    }
-  }
-
-  /** reclaim_unprotected */
-  void reclaim_unprotected(Obj* obj, ObjList& children) {
-    while (obj) {
-      auto next = obj->next();
-      (*(obj->reclaim()))(obj, children);
+      auto& list = cond(obj) ? match : nomatch;
+      list.push(obj);
       obj = next;
     }
   }
@@ -572,14 +649,6 @@
     }
   }
 
-  Rec* head() const noexcept {
-    return hazptrs_.load(std::memory_order_acquire);
-  }
-
-  int hcount() const noexcept {
-    return hcount_.load(std::memory_order_acquire);
-  }
-
   void reclaim_all_objects() {
     for (int s = 0; s < kNumShards; ++s) {
       Obj* head = untagged_[s].pop_all(RetiredList::kDontLock);
@@ -598,16 +667,10 @@
   void free_hazptr_recs() {
     /* Leak the hazard pointers for the default domain to avoid
        destruction order issues with thread caches. */
-    if (this == &default_hazptr_domain<Atom>()) {
+    if (is_default_domain()) {
       return;
     }
-    auto rec = head();
-    while (rec) {
-      auto next = rec->next();
-      rec->~Rec();
-      hazptr_rec_alloc{}.deallocate(rec, 1);
-      rec = next;
-    }
+    delete hprecs_.load(std::memory_order_acquire);
   }
 
   void wait_for_zero_bulk_reclaims() {
@@ -699,60 +762,69 @@
     DCHECK(connected);
   }
 
-  Rec* create_new_hprec() {
-    auto rec = hazptr_rec_alloc{}.allocate(1);
-    new (rec) Rec();
-    rec->set_domain(this);
-    while (true) {
-      auto h = head();
-      rec->set_next(h);
-      if (hazptrs_.compare_exchange_weak(
-              h, rec, std::memory_order_release, std::memory_order_acquire)) {
-        break;
-      }
-    }
-    hcount_.fetch_add(1);
-    return rec;
+  HazptrRecArray& get_or_create_hprecs() {
+    HazptrRecArray* hprecs = hprecs_.load(std::memory_order_acquire);
+    return FOLLY_LIKELY(!!hprecs) ? *hprecs : get_or_create_hprecs_slow();
   }
+  FOLLY_NOINLINE HazptrRecArray& get_or_create_hprecs_slow() {
+    auto* instance = new HazptrRecArray(this);
+    HazptrRecArray* expected = nullptr;
 
-  bool invoke_reclamation_in_executor(int rcount) {
-    if (!std::is_same<Atom<int>, std::atomic<int>>{} ||
-        this != &default_hazptr_domain<Atom>() || !hazptr_use_executor()) {
-      return false;
-    }
-    auto fn = exec_fn_.load(std::memory_order_acquire);
-    folly::Executor::KeepAlive<> ex =
-        fn ? fn() : detail::hazptr_get_default_executor();
-    if (!ex) {
-      return false;
+    if (!hprecs_.compare_exchange_strong(
+            expected,
+            instance,
+            std::memory_order_release,
+            std::memory_order_acquire)) {
+      delete std::exchange(instance, expected);
     }
+    return *instance;
+  }
+
+  Rec* create_new_hprec() {
+    auto& hprecs = get_or_create_hprecs();
+    //  As explanation, this increment happens-before the load-relaxed in any
+    //  call to load_hazptr_vals where this newly-created hprec would need to be
+    //  seen. May ccur in some calls to make_hazard_pointer, which precede the
+    //  fence-light-seq-cst in try_protect using the created hprecs, which
+    //  synchronize-with the fence-heavy-seq-cst in do_reclamation, which
+    //  precedes the load-relaxed in load_hazptr_vals. The fences provide the
+    //  required ordering.
+    auto const idx = hcount_.fetch_add(1, std::memory_order_relaxed);
+    return &hprecs[idx];
+  }
+
+  void schedule_reclamation(int rcount) {
     auto backlog = exec_backlog_.fetch_add(1, std::memory_order_relaxed);
-    auto recl_fn = [this, rcount, ka = ex] {
+    if (backlog >= 10) {
+      hazptr_warning_executor_backlog(backlog);
+    }
+
+    Func recl_fn = [this, rcount] {
       exec_backlog_.store(0, std::memory_order_relaxed);
       do_reclamation(rcount);
     };
-    if (ex.get() == detail::hazptr_get_default_executor().get()) {
-      invoke_reclamation_may_deadlock(ex, recl_fn);
-    } else {
-      ex->add(recl_fn);
-    }
-    if (backlog >= 10) {
-      hazptr_warning_executor_backlog(backlog);
+
+    bool canUseExecutor = std::is_same<Atom<int>, std::atomic<int>>{} &&
+        is_default_domain() && hazptr_use_executor();
+    if (canUseExecutor) {
+      auto fn = exec_fn_.load(std::memory_order_acquire);
+      if (fn(std::move(recl_fn))) {
+        return;
+      }
     }
-    return true;
+
+    // NOLINTNEXTLINE(bugprone-use-after-move)
+    invoke_reclamation_may_deadlock(std::move(recl_fn));
   }
 
-  template <typename Func>
-  void invoke_reclamation_may_deadlock(
-      folly::Executor::KeepAlive<> ex, Func recl_fn) {
-    ex->add(recl_fn);
-    // This program is using the default executor, which is an
-    // inline executor. This is not necessarily a problem. But if this
-    // program encounters deadlock, then this may be the cause. Most
-    // likely this program did not call
-    // folly::enable_hazptr_thread_pool_executor (which is normally
-    // called by folly::init). If this is a problem check if your
-    // program is missing a call to folly::init or an alternative.
+  void invoke_reclamation_may_deadlock(Func recl_fn) {
+    // Reclamation is not being delegated to an executor, and being executed
+    // inline instead. This is not necessarily a problem. But if this program
+    // encounters deadlock, then this may be the cause. Most likely this program
+    // did not call folly::enable_hazptr_thread_pool_executor (which is normally
+    // called by folly::init). If this is a problem check if your program is
+    // missing a call to folly::init or an alternative.
+    detail::hazptr_inline_executor_add(std::move(recl_fn));
   }
 
   FOLLY_EXPORT FOLLY_NOINLINE void hazptr_warning_list_too_large(
@@ -775,6 +847,17 @@
   }
 }; // hazptr_domain
 
+namespace detail {
+
+template <template <typename> class Atom>
+struct default_hazptr_domain_impl : hazptr_domain<Atom> {
+  using base = hazptr_domain<Atom>;
+  using tag = typename base::default_domain_tag;
+  default_hazptr_domain_impl() noexcept : base{tag{}} {}
+};
+
+} // namespace detail
+
 /**
  *  Free functions related to hazptr domains
  */
@@ -782,23 +865,9 @@
 /** default_hazptr_domain: Returns reference to the default domain */
 
 template <template <typename> class Atom>
-struct hazptr_default_domain_helper {
-  static FOLLY_ALWAYS_INLINE hazptr_domain<Atom>& get() {
-    static hazptr_domain<Atom> domain;
-    return domain;
-  }
-};
-
-template <>
-struct hazptr_default_domain_helper<std::atomic> {
-  static FOLLY_ALWAYS_INLINE hazptr_domain<std::atomic>& get() {
-    return default_domain;
-  }
-};
-
-template <template <typename> class Atom>
 FOLLY_ALWAYS_INLINE hazptr_domain<Atom>& default_hazptr_domain() {
-  return hazptr_default_domain_helper<Atom>::get();
+  using impl = detail::default_hazptr_domain_impl<Atom>;
+  return detail::createGlobal<impl, void>();
 }
 
 template <template <typename> class Atom>
diff --git a/folly/folly/synchronization/HazptrHolder.h b/folly/folly/synchronization/HazptrHolder.h
--- a/folly/folly/synchronization/HazptrHolder.h
+++ b/folly/folly/synchronization/HazptrHolder.h
@@ -83,7 +83,7 @@
       hprec_->reset_hazptr();
       auto domain = hprec_->domain();
 #if FOLLY_HAZPTR_THR_LOCAL
-      if (FOLLY_LIKELY(domain == &default_hazptr_domain<Atom>())) {
+      if (FOLLY_LIKELY(domain->is_default_domain())) {
         if (FOLLY_LIKELY(hazptr_tc_tls<Atom>().try_put(hprec_))) {
           return;
         }
@@ -148,12 +148,14 @@
   template <typename T>
   FOLLY_ALWAYS_INLINE void reset_protection(const T* ptr) noexcept {
     auto p = static_cast<hazptr_obj<Atom>*>(const_cast<T*>(ptr));
-    DCHECK(hprec_); // UB if *this is empty
+    // UB if *this is empty
+    DCHECK(hprec_) << "initialize hazptr_holder with make_hazard_pointer()";
     hprec_->reset_hazptr(p);
   }
 
   FOLLY_ALWAYS_INLINE void reset_protection(std::nullptr_t = nullptr) noexcept {
-    DCHECK(hprec_); // UB if *this is empty
+    // UB if *this is empty
+    DCHECK(hprec_) << "initialize hazptr_holder with make_hazard_pointer()";
     hprec_->reset_hazptr();
   }
 
@@ -183,7 +185,7 @@
 FOLLY_ALWAYS_INLINE hazptr_holder<Atom> make_hazard_pointer(
     hazptr_domain<Atom>& domain) {
 #if FOLLY_HAZPTR_THR_LOCAL
-  if (FOLLY_LIKELY(&domain == &default_hazptr_domain<Atom>())) {
+  if (FOLLY_LIKELY(domain.is_default_domain())) {
     auto hprec = hazptr_tc_tls<Atom>().try_get();
     if (FOLLY_LIKELY(hprec != nullptr)) {
       return hazptr_holder<Atom>(hprec);
diff --git a/folly/folly/synchronization/HazptrObj.h b/folly/folly/synchronization/HazptrObj.h
--- a/folly/folly/synchronization/HazptrObj.h
+++ b/folly/folly/synchronization/HazptrObj.h
@@ -166,7 +166,7 @@
   void push_obj(hazptr_domain<Atom>& domain) {
     auto coh = cohort();
     if (coh) {
-      DCHECK_EQ(&domain, &default_hazptr_domain<Atom>());
+      DCHECK(domain.is_default_domain());
       coh->push_obj(this);
     } else {
       push_to_retired(domain);
@@ -337,8 +337,9 @@
       l_.push(obj);
       inc_count();
       check_threshold_push();
-      if (safe_list_top())
+      if (safe_list_top()) {
         reclaim_safe_list();
+      }
     } else {
       obj->set_next(nullptr);
       reclaim_list(obj);
@@ -350,8 +351,9 @@
     while (true) {
       Obj* top = safe_list_top();
       obj->set_next(top);
-      if (cas_safe_list_top(top, obj))
+      if (cas_safe_list_top(top, obj)) {
         return;
+      }
     }
   }
 
diff --git a/folly/folly/synchronization/HazptrRec.h b/folly/folly/synchronization/HazptrRec.h
--- a/folly/folly/synchronization/HazptrRec.h
+++ b/folly/folly/synchronization/HazptrRec.h
@@ -32,7 +32,6 @@
 class alignas(hardware_destructive_interference_size) hazptr_rec {
   Atom<const void*> hazptr_{nullptr}; // the hazard pointer
   hazptr_domain<Atom>* domain_;
-  hazptr_rec* next_; // Next in the main hazard pointer list. Immutable.
   hazptr_rec* nextAvail_{nullptr}; // Next available hazard pointer.
 
   friend class hazptr_domain<Atom>;
@@ -44,25 +43,50 @@
   template <uint8_t M, template <typename> class A>
   friend hazptr_array<M, A> make_hazard_pointer_array();
 
-  const void* hazptr() const noexcept {
-    return hazptr_.load(std::memory_order_acquire);
+  explicit hazptr_rec(hazptr_domain<Atom>* domain) noexcept : domain_{domain} {}
+
+  const void* hazptr(std::memory_order order) const noexcept {
+    return hazptr_.load(order);
   }
 
   FOLLY_ALWAYS_INLINE void reset_hazptr(const void* p = nullptr) noexcept {
-    hazptr_.store(p, std::memory_order_release);
+    //  A call to this function with a null value always ends a reader critical
+    //  section. A call with a not-null value always begins a reader critical
+    //  section and, if the call occurs in a previous reader critical section,
+    //  also ends it.
+    //
+    //  In the reclamation path, the domain:
+    //  - Extracts the list of retired pointers.
+    //  - Walks the recs and collects the set of protected pointers.
+    //  - Calculates the list of retired unprotected pointers.
+    //  - Reclaims the retired unprotected pointers.
+    //
+    //  When beginning a reader critical section, the pointer is not-null and no
+    //  memory order is needed. The store-release does not synchronize-with any
+    //  load-acquire.
+    //
+    //  When ending a reader critical section, the pointer may be either null or
+    //  not-null and a memory order is needed. The store-release synchronizes-
+    //  with the load-acquire on this same rec in subsequent reclamation path
+    //  rec walks. When that load-acquire reads-from this store-release, the
+    //  object which was previously protected by this rec and is now unprotected
+    //  by this store-release becomes eligible for reclamation, by not being
+    //  removed from the list of retired pointers. From this synchronization,
+    //  all modifications to that object during the critical section in the
+    //  reader thread happen-before reclamation in the reclaimer thread.
+    if constexpr (detail::hazptr_prefer_fence_light) {
+      asymmetric_thread_fence_traits<Atom>::light(std::memory_order_release);
+      hazptr_.store(p, std::memory_order_relaxed);
+    } else {
+      hazptr_.store(p, std::memory_order_release);
+    }
   }
 
-  hazptr_rec<Atom>* next() { return next_; }
-
   hazptr_rec<Atom>* next_avail() { return nextAvail_; }
 
-  void set_next(hazptr_rec<Atom>* rec) { next_ = rec; }
-
   void set_next_avail(hazptr_rec<Atom>* rec) { nextAvail_ = rec; }
 
   FOLLY_ALWAYS_INLINE hazptr_domain<Atom>* domain() { return domain_; }
-
-  void set_domain(hazptr_domain<Atom>* dom) { domain_ = dom; }
 }; // hazptr_rec
 
 } // namespace folly
diff --git a/folly/folly/synchronization/HazptrThreadPoolExecutor.cpp b/folly/folly/synchronization/HazptrThreadPoolExecutor.cpp
--- a/folly/folly/synchronization/HazptrThreadPoolExecutor.cpp
+++ b/folly/folly/synchronization/HazptrThreadPoolExecutor.cpp
@@ -28,9 +28,12 @@
       std::make_shared<folly::NamedThreadFactory>("hazptr-tpe-"));
 });
 
-folly::Executor::KeepAlive<> get_hazptr_tpe() {
-  auto ex = hazptr_tpe_.try_get();
-  return ex ? ex.get() : nullptr;
+bool schedule_fn(folly::Function<void()>&& fn) {
+  if (auto ex = hazptr_tpe_.try_get()) {
+    ex->add(std::move(fn));
+    return true;
+  }
+  return false;
 }
 
 } // namespace
@@ -39,7 +42,7 @@
 
 void enable_hazptr_thread_pool_executor() {
   if (hazptr_use_executor()) {
-    default_hazptr_domain().set_executor(&get_hazptr_tpe);
+    default_hazptr_domain().set_executor(&schedule_fn);
   }
 }
 
diff --git a/folly/folly/synchronization/Lock.h b/folly/folly/synchronization/Lock.h
--- a/folly/folly/synchronization/Lock.h
+++ b/folly/folly/synchronization/Lock.h
@@ -109,41 +109,41 @@
   owner_type state_{};
 
  public:
-  FOLLY_NODISCARD lock_base() = default;
-  FOLLY_NODISCARD lock_base(lock_base&& that) noexcept
+  [[nodiscard]] lock_base() = default;
+  [[nodiscard]] lock_base(lock_base&& that) noexcept
       : mutex_{std::exchange(that.mutex_, nullptr)},
         state_{std::exchange(that.state_, owner_type{})} {}
   template <typename M = mutex_type, if_<!has_state_, M>* = nullptr>
-  FOLLY_NODISCARD lock_base(type_t<M>& mutex, std::adopt_lock_t)
+  [[nodiscard]] lock_base(type_t<M>& mutex, std::adopt_lock_t)
       : mutex_{std::addressof(mutex)}, state_{owner_true_(tag<owner_type>)} {}
   template <typename M = mutex_type, if_<has_state_, M>* = nullptr>
-  FOLLY_NODISCARD lock_base(
+  [[nodiscard]] lock_base(
       type_t<M>& mutex, std::adopt_lock_t, owner_type const& state)
       : mutex_{std::addressof(mutex)}, state_{state} {
     state_ || (check_fail_<true>(), 0);
   }
   template <typename M = mutex_type, if_<has_state_, M>* = nullptr>
-  FOLLY_NODISCARD lock_base(
+  [[nodiscard]] lock_base(
       type_t<M>& mutex, adopt_lock_state_t, owner_type const& state)
       : lock_base{mutex, std::adopt_lock, state} {}
-  FOLLY_NODISCARD explicit lock_base(mutex_type& mutex)
+  [[nodiscard]] explicit lock_base(mutex_type& mutex)
       : mutex_{std::addressof(mutex)} {
     lock();
   }
   lock_base(mutex_type& mutex, std::defer_lock_t) noexcept
       : mutex_{std::addressof(mutex)} {}
-  FOLLY_NODISCARD lock_base(mutex_type& mutex, std::try_to_lock_t)
+  [[nodiscard]] lock_base(mutex_type& mutex, std::try_to_lock_t)
       : mutex_{std::addressof(mutex)} {
     try_lock();
   }
   template <typename Rep, typename Period>
-  FOLLY_NODISCARD lock_base(
+  [[nodiscard]] lock_base(
       mutex_type& mutex, std::chrono::duration<Rep, Period> const& timeout)
       : mutex_{std::addressof(mutex)} {
     try_lock_for(timeout);
   }
   template <typename Clock, typename Duration>
-  FOLLY_NODISCARD lock_base(
+  [[nodiscard]] lock_base(
       mutex_type& mutex,
       std::chrono::time_point<Clock, Duration> const& deadline)
       : mutex_{std::addressof(mutex)} {
@@ -608,10 +608,8 @@
   }
 }
 template <
-    template <typename>
-    class To,
-    template <typename>
-    class From,
+    template <typename> class To,
+    template <typename> class From,
     typename Mutex,
     typename Transition,
     typename... A>
@@ -668,11 +666,9 @@
 //  Represents an atomic transition from the from-lock to the to-lock. Waits
 //  unboundedly for the transition to become available.
 template <
-    template <typename>
-    class ToLock,
+    template <typename> class ToLock,
     typename Mutex,
-    template <typename>
-    class FromLock>
+    template <typename> class FromLock>
 ToLock<Mutex> transition_lock(FromLock<Mutex>& lock) {
   using policy = detail::transition_lock_policy<FromLock<Mutex>, ToLock<Mutex>>;
   auto _ = typename policy::transition_fn{};
@@ -684,11 +680,9 @@
 //  Represents an atomic transition attempt from the from-lock to the to-lock.
 //  Does not wait if the transition is not immediately available.
 template <
-    template <typename>
-    class ToLock,
+    template <typename> class ToLock,
     typename Mutex,
-    template <typename>
-    class FromLock>
+    template <typename> class FromLock>
 ToLock<Mutex> try_transition_lock(FromLock<Mutex>& lock) {
   using policy = detail::transition_lock_policy<FromLock<Mutex>, ToLock<Mutex>>;
   auto _ = typename policy::try_transition_fn{};
@@ -701,11 +695,9 @@
 //  bounded by a timeout. Waits up to the timeout for the transition to become
 //  available.
 template <
-    template <typename>
-    class ToLock,
+    template <typename> class ToLock,
     typename Mutex,
-    template <typename>
-    class FromLock,
+    template <typename> class FromLock,
     typename Rep,
     typename Period>
 ToLock<Mutex> try_transition_lock_for(
@@ -721,11 +713,9 @@
 //  bounded by a deadline. Waits up to the deadline for the transition to become
 //  available.
 template <
-    template <typename>
-    class ToLock,
+    template <typename> class ToLock,
     typename Mutex,
-    template <typename>
-    class FromLock,
+    template <typename> class FromLock,
     typename Clock,
     typename Duration>
 ToLock<Mutex> try_transition_lock_until(
diff --git a/folly/folly/synchronization/MicroSpinLock.h b/folly/folly/synchronization/MicroSpinLock.h
--- a/folly/folly/synchronization/MicroSpinLock.h
+++ b/folly/folly/synchronization/MicroSpinLock.h
@@ -147,7 +147,7 @@
 
 //////////////////////////////////////////////////////////////////////
 
-typedef std::lock_guard<MicroSpinLock> MSLGuard;
+using MSLGuard = std::lock_guard<MicroSpinLock>;
 
 //////////////////////////////////////////////////////////////////////
 
diff --git a/folly/folly/synchronization/RWSpinLock.h b/folly/folly/synchronization/RWSpinLock.h
--- a/folly/folly/synchronization/RWSpinLock.h
+++ b/folly/folly/synchronization/RWSpinLock.h
@@ -49,34 +49,7 @@
  *  READ, although it is slower on WRITE, and also inherently unfair
  *  to writers.
  *
- *  RWTicketSpinLock shows more balanced READ/WRITE performance.  If
- *  your application really needs a lot more threads, and a
- *  higher-priority writer, prefer one of the RWTicketSpinLock locks.
- *
- *  Caveats:
- *
- *    RWTicketSpinLock locks can only be used with GCC on x86/x86-64
- *    based systems.
- *
- *    RWTicketSpinLock<32> only allows up to 2^8 - 1 concurrent
- *    readers and writers.
- *
- *    RWTicketSpinLock<64> only allows up to 2^16 - 1 concurrent
- *    readers and writers.
- *
- *    RWTicketSpinLock<..., true> (kFavorWriter = true, that is, strict
- *    writer priority) is NOT reentrant, even for lock_shared().
- *
- *    The lock will not grant any new shared (read) accesses while a thread
- *    attempting to acquire the lock in write mode is blocked. (That is,
- *    if the lock is held in shared mode by N threads, and a thread attempts
- *    to acquire it in write mode, no one else can acquire it in shared mode
- *    until these N threads release the lock and then the blocked thread
- *    acquires and releases the exclusive lock.) This also applies for
- *    attempts to reacquire the lock in shared mode by threads that already
- *    hold it in shared mode, making the lock non-reentrant.
- *
- *    RWSpinLock handles 2^30 - 1 concurrent readers.
+ *  RWSpinLock handles 2^30 - 1 concurrent readers.
  */
 
 #pragma once
@@ -92,14 +65,6 @@
 -------------------------------------------------------------------------------
 *      BM_RWSpinLockRead                     100000  1.786 ms  17.86 ns   53.4M
 +30.5% BM_RWSpinLockWrite                    100000  2.331 ms  23.31 ns  40.91M
-+85.7% BM_RWTicketSpinLock32Read             100000  3.317 ms  33.17 ns  28.75M
-+96.0% BM_RWTicketSpinLock32Write            100000    3.5 ms     35 ns  27.25M
-+85.6% BM_RWTicketSpinLock64Read             100000  3.315 ms  33.15 ns  28.77M
-+96.0% BM_RWTicketSpinLock64Write            100000    3.5 ms     35 ns  27.25M
-+85.7% BM_RWTicketSpinLock32FavorWriterRead  100000  3.317 ms  33.17 ns  28.75M
-+29.7% BM_RWTicketSpinLock32FavorWriterWrite 100000  2.316 ms  23.16 ns  41.18M
-+85.3% BM_RWTicketSpinLock64FavorWriterRead  100000  3.309 ms  33.09 ns  28.82M
-+30.2% BM_RWTicketSpinLock64FavorWriterWrite 100000  2.325 ms  23.25 ns  41.02M
 + 175% BM_PThreadRWMutexRead                 100000  4.917 ms  49.17 ns   19.4M
 + 166% BM_PThreadRWMutexWrite                100000  4.757 ms  47.57 ns  20.05M
 
@@ -110,24 +75,18 @@
 ---------- 8  threads ------------
 RWSpinLock       Write       142666     220ns      78ns      40.8us     269ns
 RWSpinLock       Read        1282297    222ns      80ns      37.7us     248ns
-RWTicketSpinLock Write       85692      209ns      71ns      17.9us     252ns
-RWTicketSpinLock Read        769571     215ns      78ns      33.4us     251ns
 pthread_rwlock_t Write       84248      2.48us     99ns      269us      8.19us
 pthread_rwlock_t Read        761646     933ns      101ns     374us      3.25us
 
 ---------- 16 threads ------------
 RWSpinLock       Write       124236     237ns      78ns      261us      801ns
 RWSpinLock       Read        1115807    236ns      78ns      2.27ms     2.17us
-RWTicketSpinLock Write       81781      231ns      71ns      31.4us     351ns
-RWTicketSpinLock Read        734518     238ns      78ns      73.6us     379ns
 pthread_rwlock_t Write       83363      7.12us     99ns      785us      28.1us
 pthread_rwlock_t Read        754978     2.18us     101ns     1.02ms     14.3us
 
 ---------- 50 threads ------------
 RWSpinLock       Write       131142     1.37us     82ns      7.53ms     68.2us
 RWSpinLock       Read        1181240    262ns      78ns      6.62ms     12.7us
-RWTicketSpinLock Write       83045      397ns      73ns      7.01ms     31.5us
-RWTicketSpinLock Read        744133     386ns      78ns        11ms     31.4us
 pthread_rwlock_t Write       80849      112us      103ns     4.52ms     263us
 pthread_rwlock_t Read        728698     24us       101ns     7.28ms     194us
 
@@ -136,24 +95,6 @@
 #include <folly/Portability.h>
 #include <folly/portability/Asm.h>
 
-#if defined(__GNUC__) && (defined(__i386) || FOLLY_X64 || defined(ARCH_K8))
-#define RW_SPINLOCK_USE_X86_INTRINSIC_
-#include <x86intrin.h>
-#elif defined(_MSC_VER) && defined(FOLLY_X64)
-#define RW_SPINLOCK_USE_X86_INTRINSIC_
-#elif FOLLY_AARCH64
-#define RW_SPINLOCK_USE_X86_INTRINSIC_
-#else
-#undef RW_SPINLOCK_USE_X86_INTRINSIC_
-#endif
-
-// iOS doesn't define _mm_cvtsi64_si128 and friends
-#if (FOLLY_SSE >= 2) && !FOLLY_MOBILE && FOLLY_X64
-#define RW_SPINLOCK_USE_SSE_INSTRUCTIONS_
-#else
-#undef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_
-#endif
-
 #include <algorithm>
 #include <atomic>
 #include <thread>
@@ -179,7 +120,7 @@
  * interfaces.
  */
 class RWSpinLock {
-  enum : int32_t { READER = 4, UPGRADED = 2, WRITER = 1 };
+  enum : int32_t { SHARED = 4, UPGRADE = 2, EXCLUSIVE = 1 };
 
  public:
   constexpr RWSpinLock() : bits_(0) {}
@@ -197,10 +138,10 @@
     }
   }
 
-  // Writer is responsible for clearing up both the UPGRADED and WRITER bits.
+  // Writer is responsible for clearing up both the UPGRADE and EXCLUSIVE bits.
   void unlock() {
-    static_assert(READER > WRITER + UPGRADED, "wrong bits!");
-    bits_.fetch_and(~(WRITER | UPGRADED), std::memory_order_release);
+    static_assert(SHARED > EXCLUSIVE + UPGRADE, "wrong bits!");
+    bits_.fetch_and(~(EXCLUSIVE | UPGRADE), std::memory_order_release);
   }
 
   // SharedLockable Concept
@@ -213,11 +154,11 @@
     }
   }
 
-  void unlock_shared() { bits_.fetch_add(-READER, std::memory_order_release); }
+  void unlock_shared() { bits_.fetch_add(-SHARED, std::memory_order_release); }
 
   // Downgrade the lock from writer status to reader status.
   void unlock_and_lock_shared() {
-    bits_.fetch_add(READER, std::memory_order_acquire);
+    bits_.fetch_add(SHARED, std::memory_order_acquire);
     unlock();
   }
 
@@ -232,7 +173,7 @@
   }
 
   void unlock_upgrade() {
-    bits_.fetch_add(-UPGRADED, std::memory_order_acq_rel);
+    bits_.fetch_add(-UPGRADE, std::memory_order_acq_rel);
   }
 
   // unlock upgrade and try to acquire write lock
@@ -247,36 +188,36 @@
 
   // unlock upgrade and read lock atomically
   void unlock_upgrade_and_lock_shared() {
-    bits_.fetch_add(READER - UPGRADED, std::memory_order_acq_rel);
+    bits_.fetch_add(SHARED - UPGRADE, std::memory_order_acq_rel);
   }
 
   // write unlock and upgrade lock atomically
   void unlock_and_lock_upgrade() {
-    // need to do it in two steps here -- as the UPGRADED bit might be OR-ed at
+    // need to do it in two steps here -- as the UPGRADE bit might be OR-ed at
     // the same time when other threads are trying do try_lock_upgrade().
-    bits_.fetch_or(UPGRADED, std::memory_order_acquire);
-    bits_.fetch_add(-WRITER, std::memory_order_release);
+    bits_.fetch_or(UPGRADE, std::memory_order_acquire);
+    bits_.fetch_add(-EXCLUSIVE, std::memory_order_release);
   }
 
   // Attempt to acquire writer permission. Return false if we didn't get it.
   bool try_lock() {
     int32_t expect = 0;
     return bits_.compare_exchange_strong(
-        expect, WRITER, std::memory_order_acq_rel);
+        expect, EXCLUSIVE, std::memory_order_acq_rel);
   }
 
   // Try to get reader permission on the lock. This can fail if we
   // find out someone is a writer or upgrader.
-  // Setting the UPGRADED bit would allow a writer-to-be to indicate
+  // Setting the UPGRADE bit would allow a writer-to-be to indicate
   // its intention to write and block any new readers while waiting
   // for existing readers to finish and release their read locks. This
   // helps avoid starving writers (promoted from upgraders).
   bool try_lock_shared() {
     // fetch_add is considerably (100%) faster than compare_exchange,
     // so here we are optimizing for the common (lock success) case.
-    int32_t value = bits_.fetch_add(READER, std::memory_order_acquire);
-    if (FOLLY_UNLIKELY(value & (WRITER | UPGRADED))) {
-      bits_.fetch_add(-READER, std::memory_order_release);
+    int32_t value = bits_.fetch_add(SHARED, std::memory_order_acquire);
+    if (FOLLY_UNLIKELY(value & (EXCLUSIVE | UPGRADE))) {
+      bits_.fetch_add(-SHARED, std::memory_order_release);
       return false;
     }
     return true;
@@ -284,20 +225,20 @@
 
   // try to unlock upgrade and write lock atomically
   bool try_unlock_upgrade_and_lock() {
-    int32_t expect = UPGRADED;
+    int32_t expect = UPGRADE;
     return bits_.compare_exchange_strong(
-        expect, WRITER, std::memory_order_acq_rel);
+        expect, EXCLUSIVE, std::memory_order_acq_rel);
   }
 
   // try to acquire an upgradable lock.
   bool try_lock_upgrade() {
-    int32_t value = bits_.fetch_or(UPGRADED, std::memory_order_acquire);
+    int32_t value = bits_.fetch_or(UPGRADE, std::memory_order_acquire);
 
-    // Note: when failed, we cannot flip the UPGRADED bit back,
+    // Note: when failed, we cannot flip the UPGRADE bit back,
     // as in this case there is either another upgrade lock or a write lock.
     // If it's a write lock, the bit will get cleared up when that lock's done
     // with unlock().
-    return ((value & (UPGRADED | WRITER)) == 0);
+    return ((value & (UPGRADE | EXCLUSIVE)) == 0);
   }
 
   // mainly for debugging purposes.
@@ -307,243 +248,4 @@
   std::atomic<int32_t> bits_;
 };
 
-#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
-// A more balanced Read-Write spin lock implemented based on GCC intrinsics.
-
-namespace detail {
-template <size_t kBitWidth>
-struct RWTicketIntTrait {
-  static_assert(
-      kBitWidth == 32 || kBitWidth == 64,
-      "bit width has to be either 32 or 64 ");
-};
-
-template <>
-struct RWTicketIntTrait<64> {
-  typedef uint64_t FullInt;
-  typedef uint32_t HalfInt;
-  typedef uint16_t QuarterInt;
-
-#ifdef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_
-  static __m128i make128(const uint16_t v[4]) {
-    return _mm_set_epi16(
-        0, 0, 0, 0, short(v[3]), short(v[2]), short(v[1]), short(v[0]));
-  }
-  static inline __m128i fromInteger(uint64_t from) {
-    return _mm_cvtsi64_si128(int64_t(from));
-  }
-  static inline uint64_t toInteger(__m128i in) {
-    return uint64_t(_mm_cvtsi128_si64(in));
-  }
-  static inline uint64_t addParallel(__m128i in, __m128i kDelta) {
-    return toInteger(_mm_add_epi16(in, kDelta));
-  }
-#endif
-};
-
-template <>
-struct RWTicketIntTrait<32> {
-  typedef uint32_t FullInt;
-  typedef uint16_t HalfInt;
-  typedef uint8_t QuarterInt;
-
-#ifdef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_
-  static __m128i make128(const uint8_t v[4]) {
-    // clang-format off
-    return _mm_set_epi8(
-        0, 0, 0, 0,
-        0, 0, 0, 0,
-        0, 0, 0, 0,
-        char(v[3]), char(v[2]), char(v[1]), char(v[0]));
-    // clang-format on
-  }
-  static inline __m128i fromInteger(uint32_t from) {
-    return _mm_cvtsi32_si128(int32_t(from));
-  }
-  static inline uint32_t toInteger(__m128i in) {
-    return uint32_t(_mm_cvtsi128_si32(in));
-  }
-  static inline uint32_t addParallel(__m128i in, __m128i kDelta) {
-    return toInteger(_mm_add_epi8(in, kDelta));
-  }
-#endif
-};
-} // namespace detail
-
-template <size_t kBitWidth, bool kFavorWriter = false>
-class RWTicketSpinLockT {
-  typedef detail::RWTicketIntTrait<kBitWidth> IntTraitType;
-  typedef typename detail::RWTicketIntTrait<kBitWidth>::FullInt FullInt;
-  typedef typename detail::RWTicketIntTrait<kBitWidth>::HalfInt HalfInt;
-  typedef typename detail::RWTicketIntTrait<kBitWidth>::QuarterInt QuarterInt;
-
-  union RWTicket {
-    constexpr RWTicket() : whole(0) {}
-    FullInt whole;
-    HalfInt readWrite;
-    __extension__ struct {
-      QuarterInt write;
-      QuarterInt read;
-      QuarterInt users;
-    };
-  } ticket;
-
- private: // Some x64-specific utilities for atomic access to ticket.
-  template <class T>
-  static T load_acquire(T* addr) {
-    T t = *addr; // acquire barrier
-    asm_volatile_memory();
-    return t;
-  }
-
-  template <class T>
-  static void store_release(T* addr, T v) {
-    asm_volatile_memory();
-    *addr = v; // release barrier
-  }
-
- public:
-  constexpr RWTicketSpinLockT() {}
-
-  RWTicketSpinLockT(RWTicketSpinLockT const&) = delete;
-  RWTicketSpinLockT& operator=(RWTicketSpinLockT const&) = delete;
-
-  void lock() {
-    if (kFavorWriter) {
-      writeLockAggressive();
-    } else {
-      writeLockNice();
-    }
-  }
-
-  /*
-   * Both try_lock and try_lock_shared diverge in our implementation from the
-   * lock algorithm described in the link above.
-   *
-   * In the read case, it is undesirable that the readers could wait
-   * for another reader (before increasing ticket.read in the other
-   * implementation).  Our approach gives up on
-   * first-come-first-serve, but our benchmarks showed improve
-   * performance for both readers and writers under heavily contended
-   * cases, particularly when the number of threads exceeds the number
-   * of logical CPUs.
-   *
-   * We have writeLockAggressive() using the original implementation
-   * for a writer, which gives some advantage to the writer over the
-   * readers---for that path it is guaranteed that the writer will
-   * acquire the lock after all the existing readers exit.
-   */
-  bool try_lock() {
-    RWTicket t;
-    FullInt old = t.whole = load_acquire(&ticket.whole);
-    if (t.users != t.write) {
-      return false;
-    }
-    ++t.users;
-    return __sync_bool_compare_and_swap(&ticket.whole, old, t.whole);
-  }
-
-  /*
-   * Call this if you want to prioritize writer to avoid starvation.
-   * Unlike writeLockNice, immediately acquires the write lock when
-   * the existing readers (arriving before the writer) finish their
-   * turns.
-   */
-  void writeLockAggressive() {
-    // std::this_thread::yield() is needed here to avoid a pathology if the
-    // number of threads attempting concurrent writes is >= the number of real
-    // cores allocated to this process. This is less likely than the
-    // corresponding situation in lock_shared(), but we still want to
-    // avoid it
-    uint_fast32_t count = 0;
-    QuarterInt val = __sync_fetch_and_add(&ticket.users, 1);
-    while (val != load_acquire(&ticket.write)) {
-      asm_volatile_pause();
-      if (FOLLY_UNLIKELY(++count > 1000)) {
-        std::this_thread::yield();
-      }
-    }
-  }
-
-  // Call this when the writer should be nicer to the readers.
-  void writeLockNice() {
-    // Here it doesn't cpu-relax the writer.
-    //
-    // This is because usually we have many more readers than the
-    // writers, so the writer has less chance to get the lock when
-    // there are a lot of competing readers.  The aggressive spinning
-    // can help to avoid starving writers.
-    //
-    // We don't worry about std::this_thread::yield() here because the caller
-    // has already explicitly abandoned fairness.
-    while (!try_lock()) {
-    }
-  }
-
-  // Atomically unlock the write-lock from writer and acquire the read-lock.
-  void unlock_and_lock_shared() {
-    QuarterInt val = __sync_fetch_and_add(&ticket.read, 1);
-  }
-
-  // Release writer permission on the lock.
-  void unlock() {
-    RWTicket t;
-    t.whole = load_acquire(&ticket.whole);
-
-#ifdef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_
-    FullInt old = t.whole;
-    // SSE2 can reduce the lock and unlock overhead by 10%
-    static const QuarterInt kDeltaBuf[4] = {1, 1, 0, 0}; // write/read/user
-    static const __m128i kDelta = IntTraitType::make128(kDeltaBuf);
-    __m128i m = IntTraitType::fromInteger(old);
-    t.whole = IntTraitType::addParallel(m, kDelta);
-#else
-    ++t.read;
-    ++t.write;
-#endif
-    store_release(&ticket.readWrite, t.readWrite);
-  }
-
-  void lock_shared() {
-    // std::this_thread::yield() is important here because we can't grab the
-    // shared lock if there is a pending writeLockAggressive, so we
-    // need to let threads that already have a shared lock complete
-    uint_fast32_t count = 0;
-    while (!LIKELY(try_lock_shared())) {
-      asm_volatile_pause();
-      if (FOLLY_UNLIKELY((++count & 1023) == 0)) {
-        std::this_thread::yield();
-      }
-    }
-  }
-
-  bool try_lock_shared() {
-    RWTicket t, old;
-    old.whole = t.whole = load_acquire(&ticket.whole);
-    old.users = old.read;
-#ifdef RW_SPINLOCK_USE_SSE_INSTRUCTIONS_
-    // SSE2 may reduce the total lock and unlock overhead by 10%
-    static const QuarterInt kDeltaBuf[4] = {0, 1, 1, 0}; // write/read/user
-    static const __m128i kDelta = IntTraitType::make128(kDeltaBuf);
-    __m128i m = IntTraitType::fromInteger(old.whole);
-    t.whole = IntTraitType::addParallel(m, kDelta);
-#else
-    ++t.read;
-    ++t.users;
-#endif
-    return __sync_bool_compare_and_swap(&ticket.whole, old.whole, t.whole);
-  }
-
-  void unlock_shared() { __sync_fetch_and_add(&ticket.write, 1); }
-};
-
-typedef RWTicketSpinLockT<32> RWTicketSpinLock32;
-typedef RWTicketSpinLockT<64> RWTicketSpinLock64;
-
-#endif // RW_SPINLOCK_USE_X86_INTRINSIC_
-
 } // namespace folly
-
-#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
-#undef RW_SPINLOCK_USE_X86_INTRINSIC_
-#endif
diff --git a/folly/folly/synchronization/Rcu.h b/folly/folly/synchronization/Rcu.h
--- a/folly/folly/synchronization/Rcu.h
+++ b/folly/folly/synchronization/Rcu.h
@@ -382,15 +382,16 @@
     if (time > syncTime + syncTimePeriod_ &&
         syncTime_.compare_exchange_strong(
             syncTime, time, std::memory_order_relaxed)) {
-      list_head finished;
-      {
-        std::lock_guard g(syncMutex_);
+      std::unique_lock g(syncMutex_, std::try_to_lock);
+      if (g.owns_lock()) {
+        list_head finished;
         half_sync(false, finished);
+        g.unlock();
+        // callbacks are called outside of syncMutex_
+        finished.forEach([&](list_node* item) {
+          executor_->add(std::move(item->cb_));
+        });
       }
-      // callbacks are called outside of syncMutex_
-      finished.forEach([&](list_node* item) {
-        executor_->add(std::move(item->cb_));
-      });
     }
   }
 
@@ -533,7 +534,7 @@
 // Free-function retire.  Always allocates.
 //
 // This will invoke the deleter d(p) asynchronously some time after all
-// pre-existing RCU readers have completed.  See synchronize_rcu() for more
+// pre-existing RCU readers have completed.  See rcu_synchronize() for more
 // information about RCU readers and domains.
 template <typename T, typename D = std::default_delete<T>>
 void rcu_retire(T* p, D d = {}, rcu_domain& domain = rcu_default_domain()) {
diff --git a/folly/folly/synchronization/RelaxedAtomic.h b/folly/folly/synchronization/RelaxedAtomic.h
--- a/folly/folly/synchronization/RelaxedAtomic.h
+++ b/folly/folly/synchronization/RelaxedAtomic.h
@@ -43,6 +43,8 @@
   using value_type = T;
 
   using atomic::atomic;
+  using atomic::is_always_lock_free;
+  using atomic::is_lock_free;
 
   T operator=(T desired) noexcept {
     store(desired);
@@ -52,9 +54,6 @@
     store(desired);
     return desired;
   }
-
-  bool is_lock_free() const noexcept { return atomic::is_lock_free(); }
-  bool is_lock_free() const volatile noexcept { return atomic::is_lock_free(); }
 
   void store(T desired) noexcept {
     atomic::store(desired, std::memory_order_relaxed);
diff --git a/folly/folly/synchronization/SaturatingSemaphore.h b/folly/folly/synchronization/SaturatingSemaphore.h
--- a/folly/folly/synchronization/SaturatingSemaphore.h
+++ b/folly/folly/synchronization/SaturatingSemaphore.h
@@ -23,6 +23,7 @@
 #include <folly/Likely.h>
 #include <folly/detail/Futex.h>
 #include <folly/detail/MemoryIdler.h>
+#include <folly/lang/Switch.h>
 #include <folly/portability/Asm.h>
 #include <folly/synchronization/AtomicUtil.h>
 #include <folly/synchronization/WaitOptions.h>
@@ -275,24 +276,34 @@
 FOLLY_NOINLINE bool SaturatingSemaphore<MayBlock, Atom>::tryWaitSlow(
     const std::chrono::time_point<Clock, Duration>& deadline,
     const WaitOptions& opt) noexcept {
-  switch (detail::spin_pause_until(deadline, opt, [this] { return ready(); })) {
-    case detail::spin_result::success:
-      return true;
-    case detail::spin_result::timeout:
-      return false;
-    case detail::spin_result::advance:
-      break;
-  }
-
-  if (!MayBlock) {
-    switch (detail::spin_yield_until(deadline, [this] { return ready(); })) {
+  FOLLY_EXHAUSTIVE_SWITCH({
+    switch (detail::spin_pause_until(deadline, opt, [this] {
+      return ready();
+    })) {
       case detail::spin_result::success:
         return true;
       case detail::spin_result::timeout:
         return false;
       case detail::spin_result::advance:
         break;
+      default:
+        folly::assume_unreachable();
     }
+  });
+
+  if (!MayBlock) {
+    FOLLY_EXHAUSTIVE_SWITCH({
+      switch (detail::spin_yield_until(deadline, [this] { return ready(); })) {
+        case detail::spin_result::success:
+          return true;
+        case detail::spin_result::timeout:
+          return false;
+        case detail::spin_result::advance:
+          break;
+        default:
+          folly::assume_unreachable();
+      }
+    });
   }
 
   auto before = state_.load(std::memory_order_relaxed);
diff --git a/folly/folly/synchronization/StripedThrottledLifoSem.cpp b/folly/folly/synchronization/StripedThrottledLifoSem.cpp
new file mode 100644
--- /dev/null
+++ b/folly/folly/synchronization/StripedThrottledLifoSem.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/synchronization/StripedThrottledLifoSem.h>
+
+#include <thread>
+
+#include <folly/portability/GFlags.h>
+#include <folly/portability/SysResource.h>
+#include <folly/system/ThreadName.h>
+#include <folly/tracing/StaticTracepoint.h>
+
+DEFINE_uint64(
+    folly_striped_throttled_lifo_sem_balancer_period_us,
+    100'000,
+    "How frequently StripedThrottledLifoSem load balancing is run");
+DEFINE_uint64(
+    folly_striped_throttled_lifo_sem_balancer_backoff_us,
+    1'000,
+    "When an iteration of StripedThrottledLifoSem load balancing actually "
+    "results in balancing operations, run the next iteration after this delay "
+    "instead of a whole period");
+
+namespace folly {
+
+class StripedThrottledLifoSemBalancer::Worker {
+ public:
+  explicit Worker(StripedThrottledLifoSemBalancer& parent)
+      : parent_(parent), thread_{[this] { loop(); }} {}
+
+  ~Worker() {
+    {
+      std::lock_guard g(parent_.mutex_);
+      shutdown_ = true;
+    }
+    parent_.cv_.notify_all();
+    thread_.join();
+  }
+
+  void loop() {
+    setThreadName("STLSBalancer");
+    // Run this thread at the lowest priority. The purpose is to ensure that
+    // there are no underutilized threads, so if the CPU is already
+    // oversubscribed it is fine to delay the iteration. This is best effort,
+    // not checking success.
+    setpriority(PRIO_PROCESS, 0, 19);
+
+    bool didTransfer = false;
+    while (true) {
+      std::unique_lock g(parent_.mutex_);
+      std::chrono::microseconds waitTime{
+          didTransfer
+              ? FLAGS_folly_striped_throttled_lifo_sem_balancer_backoff_us
+              : FLAGS_folly_striped_throttled_lifo_sem_balancer_period_us};
+      if (parent_.cv_.wait_for(g, waitTime, [this] { return shutdown_; })) {
+        break;
+      }
+
+      didTransfer = false;
+      for (const auto& [semPtr, balanceStep] : parent_.sems_) {
+        auto transfers = balanceStep();
+        FOLLY_SDT(
+            folly, striped_throttled_lifo_sem_balancer_step, semPtr, transfers);
+        didTransfer = didTransfer || transfers > 0;
+        parent_.totalTransfers_ += transfers;
+      }
+    }
+  }
+
+ private:
+  Worker(const Worker&) = delete;
+  Worker(Worker&&) = delete;
+  Worker& operator=(const Worker&) = delete;
+  Worker& operator=(Worker&&) = delete;
+
+  StripedThrottledLifoSemBalancer& parent_;
+  std::thread thread_;
+  bool shutdown_ = false;
+};
+
+/* static */ StripedThrottledLifoSemBalancer&
+StripedThrottledLifoSemBalancer::instance() {
+  static folly::Indestructible<StripedThrottledLifoSemBalancer> ret{
+      PrivateTag{}};
+  return ret;
+}
+
+/* static */ void StripedThrottledLifoSemBalancer::ensureWorker() {
+  static Worker worker{instance()};
+}
+
+} // namespace folly
diff --git a/folly/folly/synchronization/StripedThrottledLifoSem.h b/folly/folly/synchronization/StripedThrottledLifoSem.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/synchronization/StripedThrottledLifoSem.h
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <optional>
+#include <tuple>
+
+#include <folly/Function.h>
+#include <folly/GLog.h>
+#include <folly/Unit.h>
+#include <folly/container/F14Map.h>
+#include <folly/container/small_vector.h>
+#include <folly/memory/Malloc.h>
+#include <folly/synchronization/RelaxedAtomic.h>
+#include <folly/synchronization/ThrottledLifoSem.h>
+
+namespace folly {
+
+/**
+ * A striped version of ThrottledLifoSem.
+ *
+ * post() accepts an arbitrary stripe identifier; waits can consume posts from
+ * any stripe, and return the stripe identifier that was passed to the
+ * corresponding post().
+ *
+ * Waits specify a preferred stripe: posts from that stripe will be consumed
+ * before posts from other stripes.
+ *
+ * In other words, this behaves like an array of semaphores where posts are done
+ * on a specific entry while waits wait for any semaphore in the array, and
+ * return the entry from which the post was consumed.
+ *
+ * This can be used to implement queues sharded by some notion of stripe, for
+ * example based on CPU cache topology.
+ *
+ * Cross-stripe handoffs are best-effort, but not guaranteed: it is possible to
+ * have a stable state where there are both nonzero readers and positive value.
+ * This is because of a race in wait functions when trying to steal posts from
+ * other slices. For example, consider the situation where there are 2 stripes,
+ * both with no posts and no waiters. Then two threads perform concurrent
+ * post()/wait():
+ *
+ * Thread 0                             Thread 1
+ * --------                             --------
+ * wait(0):
+ *   try_wait on all stripes (fails)
+ *                                      post(1):
+ *                                        try_post on all stripes (fails)
+ *                                        post on stripe 1
+ *   wait on stripe 0
+ *
+ * In this case, the waiter on thread 0 will not wake up even though there is an
+ * available post on stripe 1, until some other operation unblocks this.
+ *
+ * This implies that if this semaphore is used to control the queue of an
+ * executor, the executor is not technically work-conserving. However, forward
+ * progress will happen as long as at least one thread eventually waits on each
+ * stripe, and if the executor has a steady submission rate such imbalanced
+ * states should only persist for very short time. This can be mitigated
+ * unconditionally by subscribing the semaphore to
+ * StripedThrottledLifoSemBalancer, which will periodically correct imbalances.
+ *
+ * StripedThrottledLifoSem optionally supports storing a per-stripe payload that
+ * is colocated with the semaphore state of the stripe, which can be useful to
+ * ensure that they share the cache line. The payload is initialized with the
+ * payloadArgs tuple passed to the constructor.
+ */
+template <class Payload = Unit>
+class StripedThrottledLifoSem {
+ public:
+  template <class PayloadArgs>
+  StripedThrottledLifoSem(
+      size_t numStripes,
+      const PayloadArgs& payloadArgs,
+      const ThrottledLifoSem::Options& options = {})
+      : numStripes_(numStripes) {
+    CHECK_GT(numStripes, 0);
+    stripes_ =
+        reinterpret_cast<Stripe*>(checkedMalloc(sizeof(Stripe) * numStripes_));
+    for (auto* stripe = stripes_; stripe != stripes_ + numStripes_; ++stripe) {
+      new (stripe) Stripe(payloadArgs, options);
+    }
+  }
+
+  explicit StripedThrottledLifoSem(
+      size_t numStripes, const ThrottledLifoSem::Options& options = {})
+      : StripedThrottledLifoSem(numStripes, std::tuple{}, options) {}
+
+  ~StripedThrottledLifoSem() {
+    uint32_t value = 0;
+    uint32_t posts = 0;
+    for (auto* stripe = stripes_; stripe != stripes_ + numStripes_; ++stripe) {
+      value += stripe->value.load();
+      posts += stripe->sem.valueGuess();
+      stripe->~Stripe();
+    }
+    DCHECK_EQ(value, posts);
+    sizedFree(stripes_, sizeof(Stripe) * numStripes_);
+  }
+
+  // TODO(ott): How to support n > 1? Would need explicit support in
+  // ThrottledLifoSem for splitting the post value.
+  bool post(size_t stripeIdx) {
+    DCHECK_LT(stripeIdx, numStripes_);
+    stripes_[stripeIdx].value.fetch_add(1, std::memory_order_release);
+
+    // Post a wake-up to the first stripe that has an available waiter, starting
+    // from the local one.
+    for (auto tryStripeIdx = stripeIdx;;) {
+      if (stripes_[tryStripeIdx].sem.try_post()) {
+        return true;
+      }
+      if (++tryStripeIdx == numStripes_) {
+        tryStripeIdx = 0;
+      }
+      if (tryStripeIdx == stripeIdx) {
+        // All stripes are saturated, post to the local one.
+        return stripes_[stripeIdx].sem.post();
+      }
+    }
+  }
+
+  std::optional<size_t> try_wait(size_t preferredStripeIdx) {
+    // If we can consume a wake-up (from any stripe) we are entitled to one
+    // post (from any stripe). In both cases we honor preferredStripeIdx.
+    for (auto tryStripeIdx = preferredStripeIdx;;) {
+      if (stripes_[tryStripeIdx].sem.try_wait()) {
+        break;
+      }
+      if (++tryStripeIdx == numStripes_) {
+        tryStripeIdx = 0;
+      }
+      if (tryStripeIdx == preferredStripeIdx) {
+        // No posts available.
+        return std::nullopt;
+      }
+    }
+
+    return consumePost(preferredStripeIdx);
+  }
+
+  size_t wait(size_t preferredStripeIdx, const WaitOptions& opt = {}) {
+    auto const deadline = std::chrono::steady_clock::time_point::max();
+    auto res = try_wait_until(preferredStripeIdx, deadline, opt);
+    FOLLY_SAFE_DCHECK(res, "infinity time has passed");
+    return *res;
+  }
+
+  template <typename Rep, typename Period>
+  std::optional<size_t> try_wait_for(
+      size_t preferredStripeIdx,
+      const std::chrono::duration<Rep, Period>& timeout,
+      const WaitOptions& opt = {}) {
+    return try_wait_until(
+        preferredStripeIdx, timeout + std::chrono::steady_clock::now(), opt);
+  }
+
+  template <typename Clock, typename Duration>
+  std::optional<size_t> try_wait_until(
+      size_t preferredStripeIdx,
+      const std::chrono::time_point<Clock, Duration>& deadline,
+      const WaitOptions& opt = {}) {
+    // First see if we can get a post from any stripe.
+    if (auto res = try_wait(preferredStripeIdx)) {
+      return res;
+    }
+
+    // No luck. Wait on the local stripe.
+    if (!stripes_[preferredStripeIdx].sem.try_wait_until(deadline, opt)) {
+      return std::nullopt; // Timed out.
+    }
+    // We got a wake-up, we're entitled to a post.
+    return consumePost(preferredStripeIdx);
+  }
+
+  size_t numStripes() const { return numStripes_; }
+
+  uint32_t valueGuess() const {
+    uint32_t ret = 0;
+    for (auto* stripe = stripes_; stripe != stripes_ + numStripes_; ++stripe) {
+      ret += stripe->value.load(std::memory_order_relaxed);
+    }
+    return ret;
+  }
+
+  Payload& payload(size_t stripeIdx) {
+    DCHECK_LT(stripeIdx, numStripes_);
+    return stripes_[stripeIdx].payload;
+  }
+
+  // Run a step of load balancing: if some stripes have pending posts and others
+  // have waiters, transfer some posts from the former to the latter. This does
+  // not try to achieve perfect balance, it just makes progress towards that,
+  // and it returns the number of successful transfers. The method can be called
+  // repeatedly until no transfers happen anymore.
+  //
+  // Note that this doesn't change the value of any stripe: it only affects
+  // which waiters are awoken. The waiters will still receive the index of the
+  // stripe where the post originally happened.
+  size_t balanceStep() {
+    // Should be enough for several CPU generations.
+    constexpr size_t kInlineSize = 64;
+    // Each pair is (excess, stripe index).
+    small_vector<std::pair<int64_t, size_t>, kInlineSize> donors;
+    small_vector<std::pair<int64_t, size_t>, kInlineSize> recipients;
+
+    for (size_t i = 0; i < numStripes(); ++i) {
+      auto excess = stripes_[i].sem.excessValueGuess();
+      if (excess > 0) {
+        donors.emplace_back(excess, i);
+      } else if (excess < 0) {
+        recipients.emplace_back(excess, i);
+      }
+    }
+    std::sort(donors.begin(), donors.end(), std::greater<>{});
+    std::sort(recipients.begin(), recipients.end());
+
+    size_t transfers = 0;
+    for (size_t i = 0; i < std::min(donors.size(), recipients.size()); ++i) {
+      auto& from = stripes_[donors[i].second].sem;
+      auto& to = stripes_[recipients[i].second].sem;
+      if (from.try_wait()) {
+        if (to.try_post()) {
+          ++transfers;
+        } else {
+          // to became busy, return the post to from.
+          from.post();
+        }
+      }
+    }
+    return transfers;
+  }
+
+ private:
+  StripedThrottledLifoSem(const StripedThrottledLifoSem&) = delete;
+  StripedThrottledLifoSem(StripedThrottledLifoSem&&) = delete;
+  StripedThrottledLifoSem& operator=(const StripedThrottledLifoSem&) = delete;
+  StripedThrottledLifoSem& operator=(StripedThrottledLifoSem&&) = delete;
+
+  size_t consumePost(size_t preferredStripeIdx) {
+    // We consumed a wake-up to get here, so we are entitled to one post, but we
+    // could be racing with other waiters to get it, so spin until we find one.
+    for (auto tryStripeIdx = preferredStripeIdx;;) {
+      auto& stripe = stripes_[tryStripeIdx];
+      auto value = stripe.value.load(std::memory_order_relaxed);
+      if (value > 0) {
+        if (stripe.value.compare_exchange_weak(
+                value,
+                value - 1,
+                std::memory_order_acquire,
+                std::memory_order_relaxed)) {
+          return tryStripeIdx;
+        } else {
+          continue; // Retry same stripe.
+        }
+      }
+      if (++tryStripeIdx == numStripes_) {
+        tryStripeIdx = 0;
+      }
+    }
+  }
+
+  // Each stripe has a value (number of posts to that stripe) and a semaphore; a
+  // post will always increase the value for the requested stripe, but may post
+  // to a different stripe's semaphore if this ensures we can wake up a waiter
+  // in bounded time. Globally, it should always be the case that
+  //
+  // sum_stripe value >= sum_stripe sem
+  //
+  // and in absence of concurrent operations this should be an equality.
+  struct alignas(cacheline_align_v) Stripe {
+    template <class PayloadArgs>
+    explicit Stripe(
+        const PayloadArgs& payloadArgs,
+        const ThrottledLifoSem::Options& options)
+        : sem(options), payload(std::make_from_tuple<Payload>(payloadArgs)) {}
+
+    std::atomic<uint32_t> value{0};
+    ThrottledLifoSem sem;
+    Payload payload;
+  };
+
+  const size_t numStripes_;
+  Stripe* stripes_;
+};
+
+class StripedThrottledLifoSemBalancer {
+  struct PrivateTag {};
+
+ public:
+  explicit StripedThrottledLifoSemBalancer(PrivateTag) {}
+
+  template <class Payload>
+  static void subscribe(StripedThrottledLifoSem<Payload>& sem) {
+    ensureWorker();
+    auto& self = instance();
+    std::lock_guard g(self.mutex_);
+    auto [_, inserted] = self.sems_.try_emplace(
+        reinterpret_cast<void*>(&sem), [&sem] { return sem.balanceStep(); });
+    CHECK(inserted) << "sem already subscribed";
+  }
+
+  template <class Payload>
+  static void unsubscribe(StripedThrottledLifoSem<Payload>& sem) {
+    auto& self = instance();
+    std::lock_guard g(self.mutex_);
+    auto erased = self.sems_.erase(reinterpret_cast<void*>(&sem));
+    CHECK_EQ(erased, 1) << "sem was not subscribed";
+  }
+
+  static size_t totalTransfers() { return instance().totalTransfers_.load(); }
+
+ private:
+  class Worker;
+
+  static StripedThrottledLifoSemBalancer& instance();
+
+  // The worker thread has separate lifetime from the
+  // StripedThrottledLifoSemBalancer singleton: the latter is a leaky singleton
+  // so that it can be unconditionally accessed during shutdown, while the
+  // thread is joined on shutdown. Also, the thread is only started on the
+  // first subscribe() call: just accessing the stats should not start it.
+  static void ensureWorker();
+
+  std::mutex mutex_;
+  std::condition_variable cv_;
+  folly::F14FastMap<void*, folly::Function<int64_t() const>> sems_;
+
+  folly::relaxed_atomic<size_t> totalTransfers_{0};
+};
+
+} // namespace folly
diff --git a/folly/folly/synchronization/ThrottledLifoSem.h b/folly/folly/synchronization/ThrottledLifoSem.h
--- a/folly/folly/synchronization/ThrottledLifoSem.h
+++ b/folly/folly/synchronization/ThrottledLifoSem.h
@@ -139,6 +139,15 @@
     return static_cast<uint32_t>(state_.load() & kValueMask);
   }
 
+  // Returns the number of posts that are not assigned yet to existing
+  // waiters. This can be negative if there are more waiters than posts.
+  int64_t excessValueGuess() const {
+    uint64_t state = state_.load(std::memory_order_relaxed);
+    uint64_t value = state & kValueMask;
+    uint64_t numWaiters = state >> kNumWaitersShift;
+    return static_cast<int64_t>(value) - static_cast<int64_t>(numWaiters);
+  }
+
  private:
   friend class ThrottledLifoSemTestHelper;
 
diff --git a/folly/folly/synchronization/WaitOptions.cpp b/folly/folly/synchronization/WaitOptions.cpp
deleted file mode 100644
--- a/folly/folly/synchronization/WaitOptions.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/synchronization/WaitOptions.h>
-
-#include <folly/Portability.h>
-
-namespace folly {
-
-//
-
-} // namespace folly
diff --git a/folly/folly/synchronization/detail/AtomicUtils.h b/folly/folly/synchronization/detail/AtomicUtils.h
--- a/folly/folly/synchronization/detail/AtomicUtils.h
+++ b/folly/folly/synchronization/detail/AtomicUtils.h
@@ -35,8 +35,9 @@
     case std::memory_order_acquire:
     case std::memory_order_seq_cst:
       return successMode;
+    default:
+      assume_unreachable();
   }
-  assume_unreachable();
 }
 
 inline char const* memory_order_to_str(std::memory_order mo) {
@@ -53,6 +54,8 @@
       return "acq_rel";
     case std::memory_order_seq_cst:
       return "seq_cst";
+    default:
+      assume_unreachable();
   }
 }
 } // namespace detail
diff --git a/folly/folly/synchronization/detail/Hardware.cpp b/folly/folly/synchronization/detail/Hardware.cpp
deleted file mode 100644
--- a/folly/folly/synchronization/detail/Hardware.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/synchronization/detail/Hardware.h>
-
-#include <atomic>
-#include <cstdlib>
-#include <stdexcept>
-
-#include <boost/preprocessor/repetition/repeat.hpp>
-
-#include <folly/lang/Exception.h>
-
-#if FOLLY_X64 && defined(__RTM__)
-#include <immintrin.h>
-#define FOLLY_RTM_SUPPORT 1
-#else
-#define FOLLY_RTM_SUPPORT 0
-#endif
-
-#if FOLLY_RTM_SUPPORT
-#if defined(__GNUC__) || defined(__clang__)
-#include <cpuid.h>
-#elif defined(_MSC_VER)
-#include <intrin.h>
-#endif
-#endif
-
-namespace folly {
-
-static bool rtmEnabledImpl() {
-#if !FOLLY_RTM_SUPPORT
-
-  return false;
-
-#elif defined(__GNUC__) || defined(__clang__)
-
-  if (__get_cpuid_max(0, nullptr) < 7) {
-    // very surprising, older than Core Duo!
-    return false;
-  }
-  unsigned ax, bx, cx, dx;
-  // CPUID EAX=7, ECX=0: Extended Features
-  // EBX bit 11 -> RTM support
-  __cpuid_count(7, 0, ax, bx, cx, dx);
-  return ((bx >> 11) & 1) != 0;
-
-#elif defined(_MSC_VER)
-
-  // __cpuidex:
-  // https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=vs-2019
-  int cpui[4];
-  __cpuid(cpui, 0);
-  if (unsigned(cpui[0]) < 7) {
-    return false;
-  }
-  __cpuidex(cpui, 7, 0);
-  return ((cpui[1] >> 11) & 1) != 0;
-
-#else
-
-  return false;
-
-#endif
-}
-
-bool rtmEnabled() {
-  static std::atomic<int> result{-1};
-  auto value = result.load(std::memory_order_relaxed);
-  if (value < 0) {
-    value = int(rtmEnabledImpl());
-    result.store(value, std::memory_order_relaxed);
-  }
-  return value;
-}
-
-#if FOLLY_RTM_SUPPORT
-#define FOLLY_RTM_DISABLED_NORETURN
-#else
-#define FOLLY_RTM_DISABLED_NORETURN [[noreturn]]
-#endif
-
-FOLLY_RTM_DISABLED_NORETURN static unsigned rtmBeginFunc() {
-#if FOLLY_RTM_SUPPORT
-  return _xbegin();
-#else
-  assume_unreachable();
-#endif
-}
-
-FOLLY_RTM_DISABLED_NORETURN static void rtmEndFunc() {
-#if FOLLY_RTM_SUPPORT
-  _xend();
-#else
-  assume_unreachable();
-#endif
-}
-
-FOLLY_RTM_DISABLED_NORETURN static bool rtmTestFunc() {
-#if FOLLY_RTM_SUPPORT
-  return _xtest() != 0;
-#else
-  assume_unreachable();
-#endif
-}
-
-[[noreturn]] FOLLY_DISABLE_SANITIZERS static void rtmAbortFunc(
-    [[maybe_unused]] uint8_t status) {
-#if FOLLY_RTM_SUPPORT
-  switch (status) {
-#define FOLLY_RTM_ABORT_ONE(z, n, text) \
-  case uint8_t(n):                      \
-    _xabort(uint8_t(n));                \
-    [[fallthrough]];
-    BOOST_PP_REPEAT(256, FOLLY_RTM_ABORT_ONE, unused)
-#undef FOLLY_RTM_ABORT_ONE
-    default:
-      terminate_with<std::runtime_error>("rtm not in transaction");
-  }
-#else
-  assume_unreachable();
-#endif
-}
-
-namespace detail {
-
-unsigned rtmBeginDisabled() {
-  return kRtmDisabled;
-}
-void rtmEndDisabled() {}
-bool rtmTestDisabled() {
-  return false;
-}
-[[noreturn]] void rtmAbortDisabled(uint8_t) {
-  terminate_with<std::runtime_error>("rtm not enabled");
-}
-
-static void rewrite() {
-  if (rtmEnabled()) {
-    rtmBeginV.store(rtmBeginFunc, std::memory_order_relaxed);
-    rtmEndV.store(rtmEndFunc, std::memory_order_relaxed);
-    rtmTestV.store(rtmTestFunc, std::memory_order_relaxed);
-    rtmAbortV.store(rtmAbortFunc, std::memory_order_relaxed);
-  } else {
-    rtmBeginV.store(rtmBeginDisabled, std::memory_order_relaxed);
-    rtmEndV.store(rtmEndDisabled, std::memory_order_relaxed);
-    rtmTestV.store(rtmTestDisabled, std::memory_order_relaxed);
-    rtmAbortV.store(rtmAbortDisabled, std::memory_order_relaxed);
-  }
-}
-
-unsigned rtmBeginVE() {
-  rewrite();
-  return rtmBeginV.load(std::memory_order_relaxed)();
-}
-void rtmEndVE() {
-  rewrite();
-  rtmEndV.load(std::memory_order_relaxed)();
-}
-bool rtmTestVE() {
-  rewrite();
-  return rtmTestV.load(std::memory_order_relaxed)();
-}
-void rtmAbortVE(uint8_t status) {
-  rewrite();
-  rtmAbortV.load(std::memory_order_relaxed)(status);
-}
-
-std::atomic<unsigned (*)()> rtmBeginV{rtmBeginVE};
-std::atomic<void (*)()> rtmEndV{rtmEndVE};
-std::atomic<bool (*)()> rtmTestV{rtmTestVE};
-std::atomic<void (*)(uint8_t)> rtmAbortV{rtmAbortVE};
-
-} // namespace detail
-
-} // namespace folly
diff --git a/folly/folly/synchronization/detail/Hardware.h b/folly/folly/synchronization/detail/Hardware.h
deleted file mode 100644
--- a/folly/folly/synchronization/detail/Hardware.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <atomic>
-
-#include <folly/Portability.h>
-
-namespace folly {
-
-// Valid status values returned from rtmBegin.
-// kRtmDisabled is a new return value indicating that RTM support is unavailable
-// on the platform or the compiler.
-constexpr unsigned kRtmDisabled = static_cast<unsigned>(-2);
-constexpr unsigned kRtmBeginStarted = static_cast<unsigned>(-1);
-
-// Valid abort status bits (when the status value is not kRtmBeginStarted or
-// kRtmDisabled), defined as per the Intel RTM specifications:
-// https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions.
-constexpr unsigned kRtmAbortExplicit = 1;
-constexpr unsigned kRtmAbortRetry = 2;
-constexpr unsigned kRtmAbortConflict = 4;
-constexpr unsigned kRtmAbortCapacity = 8;
-constexpr unsigned kRtmAbortDebug = 16;
-constexpr unsigned kRtmAbortNested = 32;
-
-// False if there is no need for a dynamic check to see if
-// the current environment supports RTM
-constexpr bool kRtmSupportEnabled = kIsArchAmd64;
-
-// Check on cpu support for tsx-rtm
-extern bool rtmEnabled();
-
-namespace detail {
-
-// Use func ptrs to access the txn functions to avoid txn aborts
-// due to plt mapping.
-extern std::atomic<unsigned (*)()> rtmBeginV;
-extern std::atomic<void (*)()> rtmEndV;
-extern std::atomic<bool (*)()> rtmTestV;
-extern std::atomic<void (*)(uint8_t)> rtmAbortV;
-
-} // namespace detail
-
-inline unsigned rtmBegin() {
-  return detail::rtmBeginV.load(std::memory_order_relaxed)();
-}
-inline void rtmEnd() {
-  return detail::rtmEndV.load(std::memory_order_relaxed)();
-}
-inline bool rtmTest() {
-  return detail::rtmTestV.load(std::memory_order_relaxed)();
-}
-inline void rtmAbort(uint8_t status) {
-  return detail::rtmAbortV.load(std::memory_order_relaxed)(status);
-}
-inline uint8_t rtmStatusToAbortCode(unsigned status) {
-  return status >> 24;
-}
-
-} // namespace folly
diff --git a/folly/folly/synchronization/detail/Sleeper.cpp b/folly/folly/synchronization/detail/Sleeper.cpp
--- a/folly/folly/synchronization/detail/Sleeper.cpp
+++ b/folly/folly/synchronization/detail/Sleeper.cpp
@@ -16,8 +16,6 @@
 
 #include <folly/synchronization/detail/Sleeper.h>
 
-#include <folly/Portability.h>
-
 namespace folly {
 namespace detail {
 
diff --git a/folly/folly/system/EnvUtil.cpp b/folly/folly/system/EnvUtil.cpp
--- a/folly/folly/system/EnvUtil.cpp
+++ b/folly/folly/system/EnvUtil.cpp
@@ -37,7 +37,7 @@
     }
     key = entry.subpiece(0, equalsPosition).toString();
     value = entry.subpiece(equalsPosition + 1).toString();
-    if (data.count(key)) {
+    if (data.contains(key)) {
       throw MalformedEnvironment{to<std::string>(
           "Environment contains duplicate value for \"", key, "\"")};
     }
diff --git a/folly/folly/system/EnvUtil.h b/folly/folly/system/EnvUtil.h
--- a/folly/folly/system/EnvUtil.h
+++ b/folly/folly/system/EnvUtil.h
@@ -90,8 +90,9 @@
 // of its destruction.
 struct EnvVarSaver {
   EnvVarSaver()
-      : state_(std::make_unique<experimental::EnvironmentState>(
-            experimental::EnvironmentState::fromCurrentEnvironment())) {}
+      : state_(
+            std::make_unique<experimental::EnvironmentState>(
+                experimental::EnvironmentState::fromCurrentEnvironment())) {}
 
   EnvVarSaver(EnvVarSaver&& other) noexcept : state_(std::move(other.state_)) {}
 
diff --git a/folly/folly/system/HardwareConcurrency.cpp b/folly/folly/system/HardwareConcurrency.cpp
--- a/folly/folly/system/HardwareConcurrency.cpp
+++ b/folly/folly/system/HardwareConcurrency.cpp
@@ -16,21 +16,117 @@
 
 #include <folly/system/HardwareConcurrency.h>
 
+#include <atomic>
 #include <thread>
 
+#include <folly/Utility.h>
+#include <folly/lang/SafeAssert.h>
 #include <folly/portability/Sched.h>
 
 namespace folly {
 
-unsigned int hardware_concurrency() noexcept {
 #if defined(__linux__) && !defined(__ANDROID__)
-  cpu_set_t cpuset;
-  if (!sched_getaffinity(0, sizeof(cpuset), &cpuset)) {
-    auto count = CPU_COUNT(&cpuset);
-    if (count != 0) {
-      return count;
+namespace {
+class cpu_set_state {
+ private:
+  cpu_set_t* set_{nullptr};
+  size_t size_{0};
+  bool alloc_{false};
+
+  cpu_set_state(cpu_set_t* set, size_t size, bool alloc) noexcept
+      : set_{set}, size_{size}, alloc_{alloc} {}
+
+ public:
+  /// ask
+  ///
+  /// Returns a cpu_set_state owning or referencing a cpu_set_t filled in by a
+  /// successful call to sched_getaffinity.
+  ///
+  /// Takes an optional stack-allocated cpu_set_t, which must survive the
+  /// returned object. This can avoid one allocation, depending on the host.
+  ///
+  /// Not async-signal-safe.
+  static cpu_set_state ask(cpu_set_t* stackset = nullptr) noexcept;
+
+  cpu_set_state() noexcept = default;
+
+  cpu_set_state(cpu_set_state const& that) = delete;
+
+  cpu_set_state(cpu_set_state&& that) noexcept
+      : set_{std::exchange(that.set_, nullptr)},
+        size_{std::exchange(that.size_, 0)},
+        alloc_{std::exchange(that.alloc_, false)} {}
+
+  cpu_set_state& operator=(cpu_set_state const& that) = delete;
+
+  cpu_set_state& operator=(cpu_set_state&& that) noexcept {
+    auto tmp = std::move(that);
+    std::swap(set_, tmp.set_);
+    std::swap(size_, tmp.size_);
+    std::swap(alloc_, tmp.alloc_);
+    return *this;
+  }
+
+  ~cpu_set_state();
+
+  cpu_set_t* set_ptr() const noexcept { return set_; }
+  size_t set_size() const noexcept { return size_; }
+
+  // use alloc_size() for the CPU_..._S macros, like CPU_COUNT_S and CPU_ISSET_S
+  size_t alloc_size() const noexcept { return CPU_ALLOC_SIZE(size_); }
+
+  size_t cpu_count() const noexcept {
+    return !set_ ? 0 : CPU_COUNT_S(CPU_ALLOC_SIZE(size_), set_);
+  }
+
+  bool cpu_isset(size_t idx) const noexcept {
+    return set_ && idx < size_ && CPU_ISSET_S(idx, CPU_ALLOC_SIZE(size_), set_);
+  }
+};
+
+cpu_set_state cpu_set_state::ask(cpu_set_t* stackset) noexcept {
+  static std::atomic<std::size_t> setszc{0}; // monotonic, like in the kernel
+  auto setszcv = setszc.load(std::memory_order_relaxed);
+  auto setsz = setszcv ? setszcv : CPU_SETSIZE;
+  auto set = stackset && setsz == CPU_SETSIZE ? stackset : CPU_ALLOC(setsz);
+  while (true) {
+    FOLLY_SAFE_CHECK(!!set);
+    /// sched_getaffinity:
+    /// * returns 0 on success and -1 on failure
+    /// * fails with EINVAL if the provided set is too small
+    int const ret = sched_getaffinity(0, CPU_ALLOC_SIZE(setsz), set);
+    if (ret == 0) {
+      break;
     }
+    FOLLY_SAFE_PCHECK(errno == EINVAL);
+    CPU_FREE(set == stackset ? nullptr : set);
+    setsz *= 2;
+    set = CPU_ALLOC(setsz);
   }
+  /// atomic-max: setszc <- max(setszc, setsz)
+  while (setsz > setszcv) {
+    if (setszc.compare_exchange_strong(
+            setszcv, setsz, std::memory_order_relaxed)) {
+      setszcv = setsz;
+    }
+  }
+  FOLLY_SAFE_CHECK(setsz <= setszcv);
+  return {set, setsz, set != stackset};
+}
+
+cpu_set_state::~cpu_set_state() {
+  CPU_FREE(!alloc_ ? nullptr : set_);
+  set_ = nullptr;
+  size_ = 0;
+  alloc_ = false;
+}
+} // namespace
+#endif
+
+unsigned int available_concurrency() noexcept {
+#if defined(__linux__) && !defined(__ANDROID__)
+  cpu_set_t stackset;
+  return to_narrow(cpu_set_state::ask(&stackset).cpu_count());
 #endif
 
   return std::thread::hardware_concurrency();
diff --git a/folly/folly/system/HardwareConcurrency.h b/folly/folly/system/HardwareConcurrency.h
--- a/folly/folly/system/HardwareConcurrency.h
+++ b/folly/folly/system/HardwareConcurrency.h
@@ -16,8 +16,50 @@
 
 #pragma once
 
+#include <utility>
+
 namespace folly {
 
-unsigned int hardware_concurrency() noexcept;
+/// available_concurrency
+///
+/// Returns a number suitable for use as a size for thread-pools or as a size
+/// for cpu-striped data-structures. Consider the purpose of this function as
+/// returning some measure of the concurrency available to the calling program.
+///
+/// Caution: May fail by returning 0, which is never a success value.
+///
+/// Caution: The value returned may change over the lifetime of the process or
+/// of the system.
+///
+/// Caution: This does not necessarily return a number of processors. This is
+/// *not* an alias, for example, for get_nprocs, get_nprocs_conf, or sysconf
+/// with key any of _SC_NPROCESSORS_ONLN or _SC_NPROCESSORS_CONF.
+///
+/// Caution: May succeed but return a number that is not necessarily larger than
+/// the maximum value returnable by a call to getcpu(). For cpu-striped data
+/// structures, this means that the number returned by getcpu() may not be a
+/// valid index into the data-structure; either the result must be %'d by the
+/// number of stripes, or the number of stripes must be the bit-ceiling of the
+/// result of this function and then the result of getcpu() must be &'d. For
+/// use-cases which must be free of % and & operations, this function is not
+/// suitable.
+///
+/// Note: This may use arbitrary sources of information (signals) as inputs for
+/// calculating a suitable return value. It is not specified to use any specific
+/// signal in any specific way.
+/// * The result of std::thread::hardware_concurrency (all).
+/// * The result of get_nprocs, get_nprocs_conf, or sysconf as noted above.
+/// * The result of sched_getaffinity (linux but not android).
+/// * Unspecified others....
+///
+/// mimic: std::thread::hardware_concurrency
+unsigned int available_concurrency() noexcept;
+
+/// hardware_concurrency
+///
+/// An alias for available_concurrency.
+inline unsigned int hardware_concurrency() noexcept {
+  return available_concurrency();
+}
 
 } // namespace folly
diff --git a/folly/folly/system/MemoryMapping.cpp b/folly/folly/system/MemoryMapping.cpp
--- a/folly/folly/system/MemoryMapping.cpp
+++ b/folly/folly/system/MemoryMapping.cpp
@@ -38,7 +38,7 @@
     // Linux implementations of unmap/mlock/munlock take a kernel
     // semaphore and block other threads from doing other memory
     // operations. Split the operations in chunks.
-    ? (1 << 20) // 1MB
+    ? (2 << 20) // 2MiB - match x86 PMD size for THP compatibility.
     // MSVC doesn't have this problem, and calling munmap many times
     // with the same address is a bad idea with the windows implementation.
     : (-1);
@@ -306,9 +306,14 @@
   size_t amountSucceeded = 0;
   locked_ = memOpInChunks(
       [flags](void* addr, size_t len) -> int {
+        if (flags.tryCollapseToTHP && len >= kDefaultMlockChunkSize) {
+          if (madvise(addr, len, MADV_POPULATE_READ) == 0) {
+            madvise(addr, len, MADV_COLLAPSE);
+          }
+        }
         // If no flags are set, mlock2() behaves exactly the same as
         // mlock(). Prefer the portable variant.
-        return flags == LockFlags{}
+        return !flags.lockOnFault
             ? ::mlock(addr, len)
             : mlock2wrapper(addr, len, flags);
       },
@@ -464,9 +469,4 @@
       srcMap.range().data(),
       srcMap.range().size());
 }
-
-bool MemoryMapping::LockFlags::operator==(const LockFlags& other) const {
-  return lockOnFault == other.lockOnFault;
-}
-
 } // namespace folly
diff --git a/folly/folly/system/MemoryMapping.h b/folly/folly/system/MemoryMapping.h
--- a/folly/folly/system/MemoryMapping.h
+++ b/folly/folly/system/MemoryMapping.h
@@ -42,8 +42,6 @@
   struct LockFlags {
     LockFlags() {}
 
-    bool operator==(const LockFlags& other) const;
-
     /**
      * Instead of locking all the pages in the mapping before the call returns,
      * only lock those that are currently resident and mark the others to be
@@ -52,6 +50,17 @@
      * Uses mlock2(flags=MLOCK_ONFAULT). Requires Linux >= 4.4.
      */
     bool lockOnFault = false;
+
+    /**
+     * Call madvise(MADV_POPULATE_READ) + madvise(MADV_COLLAPSE) before mlock().
+     * See man 7 mmap for MADV_COLLAPSE details: it will synchronously allocate
+     * a THP 2MB page, fault in the existing 4KB pages and copy them into the
+     * THP page all in a best effort manner. Per the man page, at least one
+     * consituent 4KB page needs to be present but using a prior
+     * MADV_POPULATE_READ gives better chances of this collapse. On error
+     * proceeds to mlock/mlock2. Requires Linux >= 6.1.
+     */
+    bool tryCollapseToTHP = false;
   };
 
   /**
diff --git a/folly/folly/system/arch/x86.h b/folly/folly/system/arch/x86.h
new file mode 100644
--- /dev/null
+++ b/folly/folly/system/arch/x86.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+
+#include <folly/Portability.h>
+
+#if FOLLY_X86 || FOLLY_X64
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+#endif
+
+namespace folly {
+
+/// x86_cpuid
+///
+/// Wrapper around x86 instruction cpuid.
+/// * Some platforms have intrinsics but not all do.
+/// * The instruction is tricky to handle in some cases.
+///
+/// The instruction takes its arguments in eax, ecx and produces its results in
+/// eax, ebx, ecx, edx. So it is rather straightforward to wrap. But, a wrinkle:
+/// x86 PIC code uses ebx as the PIC register, so it must be preserved around
+/// the cpuid instruction.
+///
+/// This function might be used in __ifunc__ code, which runs concurrently with
+/// relocations. So using symbols with potentially external linkage, such as
+/// calling functions that might be relocated, is forbidden. Address this by
+/// by marking as [[always_inline]].
+///
+/// multi-thread-safe
+/// async-signal-safe
+/// reentrancy-safe
+/// ifunc-safe
+FOLLY_ALWAYS_INLINE void x86_cpuid(
+    unsigned int info[4],
+    [[maybe_unused]] unsigned int leaf,
+    [[maybe_unused]] unsigned int tag = 0) {
+#if FOLLY_X86 || FOLLY_X64
+#if defined(_MSC_VER)
+  __cpuidex(reinterpret_cast<int*>(info), leaf, tag); // no inline asm
+#else
+  asm volatile(
+#if defined(__pic__) && defined(__i386__)
+      // ebx is PIC register - must preserve ebx around cpuid
+      R"(
+        mov %%ebx, %[tmp]
+        cpuid
+        xchg %%ebx, %[tmp]
+      )"
+#else
+      // ebx is not special
+      R"(
+        cpuid
+      )"
+#endif
+      : // outputs
+      "=a"(info[0]),
+#if defined(__pic__) && defined(__i386__)
+      [tmp] "=&r"(info[1]), // ebx out was xchg'd to tmp; early clobber
+#else
+      "=b"(info[1]), // ebx out is in ebx
+#endif
+      "=c"(info[2]),
+      "=d"(info[3])
+      : // inputs
+      "a"(leaf),
+      "c"(tag)
+      : // clobbers
+  );
+#endif
+#else
+  info[0] = info[1] = info[2] = info[3] = 0;
+#endif
+}
+
+FOLLY_ALWAYS_INLINE unsigned int x86_cpuid_max( //
+    unsigned int leaf,
+    unsigned int* sig) {
+  unsigned int info[4];
+  x86_cpuid(info, leaf);
+  if (sig) {
+    *sig = info[1]; // ebx
+  }
+  return info[0]; // eax
+}
+
+enum class x86_cpuid_vendor { unknown, intel, amd };
+
+union x86_cpuid_vendor_name {
+  char const str[13];
+  unsigned int words[3];
+};
+inline constexpr x86_cpuid_vendor_name x86_cpuid_vendor_names[3] = {
+    {},
+    {"GenuineIntel"},
+    {"AuthenticAMD"},
+};
+
+FOLLY_ALWAYS_INLINE x86_cpuid_vendor x86_cpuid_get_vendor() {
+  if constexpr (kIsArchX86 || kIsArchAmd64) {
+    unsigned int info[4];
+    x86_cpuid(info, 0);
+    constexpr auto num_names =
+        sizeof(x86_cpuid_vendor_names) / sizeof(x86_cpuid_vendor_name);
+    for (unsigned int i = 1; i < num_names; ++i) {
+      auto& name = x86_cpuid_vendor_names[i].words;
+      if (info[1] == name[0] && info[2] == name[2] && info[3] == name[1]) {
+        return static_cast<x86_cpuid_vendor>(i);
+      }
+    }
+  }
+  return x86_cpuid_vendor::unknown;
+}
+
+struct x86_cpuid_cache_info {
+  static inline constexpr unsigned int id_count_max = 32;
+
+  unsigned int eax = 0;
+  unsigned int ebx = 0;
+  unsigned int ecx = 0;
+
+  size_t cache_type() const noexcept { return eax & 0x1F; }
+  bool cache_type_data() const noexcept { return cache_type() & 1; }
+  bool cache_type_inst() const noexcept { return cache_type() & 2; }
+  bool cache_type_null() const noexcept { return !cache_type(); }
+
+  size_t level() const noexcept { return (eax >> 5) & 0x7; }
+  size_t line_size() const noexcept { return (ebx & 0xFFF) + 1; }
+  size_t partitions() const noexcept { return ((ebx >> 12) & 0x3FF) + 1; }
+  size_t ways() const noexcept { return ((ebx >> 22) & 0x3FF) + 1; }
+  size_t sets() const noexcept { return ecx + 1; }
+  size_t cache_size() const noexcept {
+    return !cache_type_null() * ways() * partitions() * line_size() * sets();
+  }
+};
+
+FOLLY_ALWAYS_INLINE x86_cpuid_cache_info
+x86_cpuid_get_cache_info(x86_cpuid_vendor vend, unsigned int id) {
+  unsigned int info[4];
+  switch (vend) {
+    case x86_cpuid_vendor::unknown:
+      return x86_cpuid_cache_info{};
+    case x86_cpuid_vendor::intel:
+      x86_cpuid(info, /* leaf = */ 4, /* tag = */ id + 1);
+      return x86_cpuid_cache_info{info[0], info[1], info[2]};
+    case x86_cpuid_vendor::amd:
+      x86_cpuid(info, /* leaf = */ 0x8000001D, /* tag = */ id + 1);
+      return x86_cpuid_cache_info{info[0], info[1], info[2]};
+    default:
+      assert(0 && "unsupported x86 vendor");
+      return x86_cpuid_cache_info{};
+  }
+}
+
+FOLLY_ALWAYS_INLINE x86_cpuid_cache_info
+x86_cpuid_get_llc_cache_info(x86_cpuid_vendor vend) {
+  x86_cpuid_cache_info cache_info{};
+  for (unsigned int i = 0; i < x86_cpuid_cache_info::id_count_max; ++i) {
+    auto const info = x86_cpuid_get_cache_info(vend, i);
+    if (info.cache_type_null()) {
+      break;
+    }
+    if (info.cache_type_data()) {
+      cache_info = info;
+    }
+  }
+  return cache_info;
+}
+
+FOLLY_ALWAYS_INLINE x86_cpuid_cache_info x86_cpuid_get_llc_cache_info() {
+  auto const vend = x86_cpuid_get_vendor();
+  return x86_cpuid_get_llc_cache_info(vend);
+}
+
+} // namespace folly
diff --git a/folly/folly/test/DeterministicSchedule.h b/folly/folly/test/DeterministicSchedule.h
--- a/folly/folly/test/DeterministicSchedule.h
+++ b/folly/folly/test/DeterministicSchedule.h
@@ -35,7 +35,9 @@
 #include <folly/concurrency/CacheLocality.h>
 #include <folly/detail/Futex.h>
 #include <folly/lang/CustomizationPoint.h>
+#include <folly/synchronization/AsymmetricThreadFence.h>
 #include <folly/synchronization/AtomicNotification.h>
+#include <folly/synchronization/AtomicUtil.h>
 #include <folly/synchronization/detail/AtomicUtils.h>
 #include <folly/synchronization/test/Semaphore.h>
 
@@ -547,6 +549,23 @@
 
 template <typename T>
 using DeterministicAtomic = DeterministicAtomicImpl<T, DeterministicSchedule>;
+
+} // namespace test
+
+template <>
+struct atomic_thread_fence_traits<test::DeterministicAtomic> {
+  static inline constexpr auto fence =
+      test::DeterministicSchedule::atomic_thread_fence;
+};
+
+template <>
+struct asymmetric_thread_fence_traits<test::DeterministicAtomic> {
+  using traits = atomic_thread_fence_traits<test::DeterministicAtomic>;
+  static inline constexpr auto light = traits::fence;
+  static inline constexpr auto heavy = traits::fence;
+};
+
+namespace test {
 
 /* Futex extensions for DeterministicSchedule based Futexes */
 int futexWakeImpl(
diff --git a/folly/folly/testing/TestUtil.cpp b/folly/folly/testing/TestUtil.cpp
--- a/folly/folly/testing/TestUtil.cpp
+++ b/folly/folly/testing/TestUtil.cpp
@@ -117,8 +117,9 @@
 TemporaryDirectory::TemporaryDirectory(
     StringPiece namePrefix, fs::path dir, Scope scope)
     : scope_(scope),
-      path_(std::make_unique<fs::path>(
-          generateUniquePath(std::move(dir), namePrefix))) {
+      path_(
+          std::make_unique<fs::path>(
+              generateUniquePath(std::move(dir), namePrefix))) {
   fs::create_directory(path());
 }
 
@@ -231,17 +232,18 @@
   return std::string(buf.get(), size);
 }
 
-fs::path find_resource(const std::string& resource) {
+fs::path find_resource(std::string_view resource) {
   auto const pos = resource.find_last_of('/');
   if (pos == std::string::npos) {
-    throw std::invalid_argument("invalid: " + resource);
+    throw std::invalid_argument("invalid: " + std::string(resource));
   }
   auto const ext = folly::ext::test_find_resource;
   auto fn = ext //
       ? fs::path(ext(resource)) // hooked, eg via internal extension
-      : fs::executable_path().parent_path() / resource; // current cmake build
+      : fs::executable_path().parent_path() //
+          / std::string(resource); // current cmake build
   if (!fs::exists(fn)) {
-    throw std::runtime_error("missing: " + resource);
+    throw std::runtime_error("missing: " + std::string(resource));
   }
   return fn;
 }
diff --git a/folly/folly/testing/TestUtil.h b/folly/folly/testing/TestUtil.h
--- a/folly/folly/testing/TestUtil.h
+++ b/folly/folly/testing/TestUtil.h
@@ -214,7 +214,7 @@
 class CaptureFD {
  private:
   struct NoOpChunkCob {
-    void operator()(StringPiece) {}
+    void operator()(StringPiece /*unused*/) {}
   };
 
  public:
@@ -262,7 +262,7 @@
 //
 //  Care must be taken to set up the test and resource build rules in accordance
 //  with this function.
-fs::path find_resource(const std::string& resource);
+fs::path find_resource(std::string_view resource);
 
 } // namespace test
 } // namespace folly
diff --git a/folly/folly/tracing/AsyncStack.cpp b/folly/folly/tracing/AsyncStack.cpp
--- a/folly/folly/tracing/AsyncStack.cpp
+++ b/folly/folly/tracing/AsyncStack.cpp
@@ -112,7 +112,8 @@
   }
 
   void set(AsyncStackRoot* root) noexcept {
-    value.store(root, std::memory_order_release);
+    std::atomic_signal_fence(std::memory_order_release);
+    value.store(root, std::memory_order_relaxed);
   }
 
   void set_relaxed(AsyncStackRoot* root) noexcept {
diff --git a/folly/folly/tracing/AsyncStack.h b/folly/folly/tracing/AsyncStack.h
--- a/folly/folly/tracing/AsyncStack.h
+++ b/folly/folly/tracing/AsyncStack.h
@@ -178,12 +178,12 @@
 // Get access to the current thread's top-most AsyncStackRoot.
 //
 // Returns nullptr if there is no active AsyncStackRoot.
-FOLLY_NODISCARD AsyncStackRoot* tryGetCurrentAsyncStackRoot() noexcept;
+[[nodiscard]] AsyncStackRoot* tryGetCurrentAsyncStackRoot() noexcept;
 
 // Get access to the current thread's top-most AsyncStackRoot.
 //
 // Assumes that there is a current AsyncStackRoot.
-FOLLY_NODISCARD AsyncStackRoot& getCurrentAsyncStackRoot() noexcept;
+[[nodiscard]] AsyncStackRoot& getCurrentAsyncStackRoot() noexcept;
 
 // Exchange the current thread's active AsyncStackRoot with the
 // specified AsyncStackRoot pointer, returning the old AsyncStackRoot
@@ -191,7 +191,7 @@
 //
 // This is intended to be used to update the thread-local pointer
 // when context-switching fiber stacks.
-FOLLY_NODISCARD AsyncStackRoot* exchangeCurrentAsyncStackRoot(
+[[nodiscard]] AsyncStackRoot* exchangeCurrentAsyncStackRoot(
     AsyncStackRoot* newRoot) noexcept;
 
 // Perform some consistency checks on the specified AsyncStackFrame,
