packages feed

folly-clib 20250713.1537 → 20260203.1245

raw patch · 790 files changed

+28846/−12219 lines, 790 files

This diff is very large; some files are shown as “too large to diff”. Download the raw patch for the complete diff.

Files

folly-clib.cabal view
@@ -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 
folly/_build/folly/folly-config.h view
@@ -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
folly/folly/AtomicHashArray.h view
@@ -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 --
folly/folly/AtomicIntrusiveLinkedList.h view
@@ -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() {}
folly/folly/AtomicUnorderedMap.h view
@@ -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,
+ folly/folly/Benchmark.cpp view
@@ -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
folly/folly/Benchmark.h view
@@ -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) {
+ folly/folly/BenchmarkUtil.cpp view
@@ -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
folly/folly/BenchmarkUtil.h view
@@ -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
folly/folly/CPortability.h view
@@ -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
folly/folly/CancellationToken-inl.h view
@@ -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()));     }   } }
folly/folly/CancellationToken.h view
@@ -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 
folly/folly/ClockGettimeWrappers.cpp view
@@ -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;     }   } 
folly/folly/ConcurrentBitSet.h view
@@ -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;
folly/folly/ConcurrentSkipList-inl.h view
@@ -48,7 +48,7 @@   };   public:-  typedef T value_type;+  using value_type = T;    SkipListNode(const SkipListNode&) = delete;   SkipListNode& operator=(const SkipListNode&) = delete;
folly/folly/ConcurrentSkipList.h view
@@ -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();   }
folly/folly/ConstexprMath.h view
@@ -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.
folly/folly/Conv.h view
@@ -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) {
folly/folly/CppAttributes.h view
@@ -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
folly/folly/CpuId.h view
@@ -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) \
folly/folly/Demangle.cpp view
@@ -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,
folly/folly/Demangle.h view
@@ -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.
folly/folly/DynamicConverter.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/json/DynamicConverter.h>+#include <folly/json/DynamicConverter.h> // @shim
folly/folly/ExceptionWrapper-inl.h view
@@ -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>
folly/folly/ExceptionWrapper.h view
@@ -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> 
folly/folly/Executor.h view
@@ -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).
folly/folly/Expected.h view
@@ -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()));
folly/folly/FBString.h view
@@ -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(), "");
folly/folly/FBVector.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/container/FBVector.h>+#include <folly/container/FBVector.h> // @shim
folly/folly/File.cpp view
@@ -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; }
folly/folly/FileUtil.cpp view
@@ -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) {
folly/folly/FileUtil.h view
@@ -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);
folly/folly/Fingerprint.cpp view
@@ -17,7 +17,6 @@ #include <folly/Fingerprint.h>  #include <folly/Portability.h>-#include <folly/Utility.h> #include <folly/detail/FingerprintPolynomial.h>  #include <utility>
folly/folly/FollyMemcpy.cpp view
@@ -14,7 +14,7 @@  * limitations under the License.  */ -#include <cstring>+#include <cstring> // @donotremove  #if !defined(__AVX2__) && !(defined(__linux__) && defined(__aarch64__)) namespace folly {
folly/folly/FollyMemset.cpp view
@@ -14,7 +14,7 @@  * limitations under the License.  */ -#include <cstring>+#include <cstring> // @donotremove  #if !defined(__AVX2__) && !(defined(__linux__) && defined(__aarch64__)) 
folly/folly/Format.cpp view
@@ -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 {
folly/folly/Format.h view
@@ -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)...); } 
folly/folly/FormatTraits.h view
@@ -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));
folly/folly/Function.h view
@@ -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))) {
folly/folly/GroupVarint.h view
@@ -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 
folly/folly/Hash.h view
@@ -17,4 +17,4 @@ #pragma once  //  shims:-#include <folly/hash/Hash.h>+#include <folly/hash/Hash.h> // @shim
folly/folly/IPAddress.cpp view
@@ -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(
folly/folly/IPAddressV4.cpp view
@@ -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]; }
folly/folly/IPAddressV4.h view
@@ -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:
folly/folly/IPAddressV6.cpp view
@@ -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]; }
folly/folly/IPAddressV6.h view
@@ -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
folly/folly/Indestructible.h view
@@ -16,8 +16,6 @@  #pragma once -#include <cassert>-#include <new> #include <type_traits> #include <utility> 
folly/folly/IndexedMemPool.h view
@@ -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) {
folly/folly/IntrusiveList.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/container/IntrusiveList.h>+#include <folly/container/IntrusiveList.h> // @shim
folly/folly/Lazy.h view
@@ -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");
folly/folly/MPMCPipeline.h view
@@ -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);   } 
folly/folly/MPMCQueue.h view
@@ -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();
folly/folly/MapUtil.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/container/MapUtil.h>+#include <folly/container/MapUtil.h> // @shim
folly/folly/MaybeManagedPtr.h view
@@ -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.
folly/folly/Memory.h view
@@ -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
folly/folly/MicroLock.h view
@@ -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
folly/folly/ObserverContainer.h view
@@ -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;
folly/folly/OperationCancelled.h view
@@ -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";
folly/folly/Optional.h view
@@ -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();   }
folly/folly/PackedSyncPtr.h view
@@ -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:   /*
folly/folly/Padded.h view
@@ -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
folly/folly/Poly.h view
@@ -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)...); }
folly/folly/Portability.h view
@@ -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
folly/folly/ProducerConsumerQueue.h view
@@ -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;
folly/folly/Random-inl.h view
@@ -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
folly/folly/Range.h view
@@ -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); } 
folly/folly/Replaceable.h view
@@ -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));
folly/folly/ScopeGuard.h view
@@ -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{}))) {
folly/folly/SharedMutex.h view
@@ -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 {
folly/folly/Singleton-inl.h view
@@ -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<>>();
folly/folly/Singleton.cpp view
@@ -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
folly/folly/SingletonThreadLocal.h view
@@ -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;   }
folly/folly/SocketAddress.cpp view
@@ -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
folly/folly/SocketAddress.h view
@@ -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;   } 
folly/folly/String-inl.h view
@@ -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 <
folly/folly/String.cpp view
@@ -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 
folly/folly/String.h view
@@ -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
folly/folly/Subprocess.cpp view
@@ -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;
folly/folly/Subprocess.h view
@@ -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(
folly/folly/Synchronized.h view
@@ -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  /**
folly/folly/ThreadLocal.h view
@@ -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>>; 
folly/folly/Traits.h view
@@ -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 ///
folly/folly/Try.h view
@@ -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;
folly/folly/Unicode.cpp view
@@ -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
folly/folly/Unicode.h view
@@ -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.
folly/folly/Uri-inl.h view
@@ -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; }
folly/folly/Uri.cpp view
@@ -32,7 +32,7 @@  } // namespace -// private default contructor+// private default constructor Uri::Uri() : hasAuthority_(false), port_(0) {}  // public string constructor
folly/folly/Utility.h view
@@ -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__) { \
folly/folly/VirtualExecutor.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/executors/VirtualExecutor.h>+#include <folly/executors/VirtualExecutor.h> // @shim
folly/folly/algorithm/simd/Contains.h view
@@ -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.
folly/folly/algorithm/simd/Ignore.h view
@@ -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)  */
folly/folly/algorithm/simd/detail/SimdAnyOf.h view
@@ -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.
folly/folly/algorithm/simd/detail/SimdForEach.h view
@@ -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;
folly/folly/algorithm/simd/detail/SimdPlatform.h view
@@ -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
folly/folly/algorithm/simd/detail/UnrollUtils.h view
@@ -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>{}));   } };
folly/folly/base64.h view
@@ -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
folly/folly/channels/Channel-inl.h view
@@ -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>();
folly/folly/channels/ChannelProcessor-inl.h view
@@ -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()) {
folly/folly/channels/ConsumeChannel-inl.h view
@@ -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();   }
folly/folly/channels/Merge-inl.h view
@@ -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());       }
folly/folly/channels/MergeChannel-inl.h view
@@ -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()));
folly/folly/channels/MultiplexChannel-inl.h view
@@ -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 {
folly/folly/channels/ProxyChannel.h view
@@ -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();
folly/folly/channels/Transform-inl.h view
@@ -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;       }
folly/folly/channels/detail/PointerVariant.h view
@@ -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())));     }   } 
folly/folly/channels/detail/Utility.h view
@@ -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);     });   }
folly/folly/chrono/Conv.h view
@@ -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()};   } } 
folly/folly/cli/NestedCommandLineApp.cpp view
@@ -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 {
folly/folly/cli/NestedCommandLineApp.h view
@@ -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.
folly/folly/cli/ProgramOptions.cpp view
@@ -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),
folly/folly/compression/Compression.cpp view
@@ -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 =
folly/folly/compression/CompressionContextPool.h view
@@ -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_; };
folly/folly/compression/CompressionContextPoolSingletons.cpp view
@@ -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() {
folly/folly/compression/CompressionContextPoolSingletons.h view
@@ -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.
folly/folly/compression/CompressionCoreLocalContextPool.h view
@@ -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
folly/folly/compression/QuotientMultiSet-inl.h view
@@ -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);
folly/folly/compression/Zlib.cpp view
@@ -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));   } } 
folly/folly/compression/Zstd.cpp view
@@ -21,8 +21,6 @@ #include <stdexcept> #include <string> -#include <zstd.h>- #include <folly/Conv.h> #include <folly/Range.h> #include <folly/ScopeGuard.h>
folly/folly/compression/elias_fano/BitVectorCoding.h view
@@ -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),
folly/folly/compression/elias_fano/EliasFanoCoding.h view
@@ -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_;
folly/folly/concurrency/AtomicSharedPtr.h view
@@ -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
folly/folly/concurrency/CacheLocality.cpp view
@@ -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_);
folly/folly/concurrency/CacheLocality.h view
@@ -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);
folly/folly/concurrency/ConcurrentHashMap.h view
@@ -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
folly/folly/concurrency/SingletonRelaxedCounter.h view
@@ -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);   } 
folly/folly/concurrency/ThreadCachedSynchronized.h view
@@ -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;   }
folly/folly/concurrency/container/FlatCombiningPriorityQueue.h view
@@ -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(
folly/folly/concurrency/container/LockFreeRingBuffer.h view
@@ -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 {}
folly/folly/concurrency/container/RelaxedConcurrentPriorityQueue.h view
@@ -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) {
folly/folly/concurrency/container/atomic_grow_array.h view
@@ -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
folly/folly/concurrency/detail/AtomicSharedPtr-detail.h view
@@ -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);   }; 
folly/folly/concurrency/detail/ConcurrentHashMap-detail.h view
@@ -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();   }
folly/folly/concurrency/memory/PrimaryPtr.h view
@@ -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_);
+ folly/folly/container/CollectionUtil.h view
@@ -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
folly/folly/container/EvictingCacheMap.h view
@@ -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_;
folly/folly/container/F14Map.h view
@@ -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,
folly/folly/container/F14Set.h view
@@ -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,
folly/folly/container/FBVector.h view
@@ -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
folly/folly/container/HeterogeneousAccess.h view
@@ -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
folly/folly/container/IntrusiveHeap.h view
@@ -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;
folly/folly/container/IntrusiveList.h view
@@ -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>,
folly/folly/container/Iterator.h view
@@ -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
folly/folly/container/MapUtil.h view
@@ -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; } 
folly/folly/container/Merge.h view
@@ -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).
folly/folly/container/RegexMatchCache.cpp view
@@ -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)));       }     }
folly/folly/container/RegexMatchCache.h view
@@ -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 
folly/folly/container/StdBitset.h view
@@ -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]) {
folly/folly/container/detail/F14Policy.h view
@@ -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,
folly/folly/container/detail/F14Table.h view
@@ -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
folly/folly/container/detail/Util.h view
@@ -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));
folly/folly/container/heap_vector_types.h view
@@ -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;
+ folly/folly/container/irange.h view
@@ -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
folly/folly/container/small_vector.h view
@@ -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 ||
folly/folly/container/sorted_vector_types.h view
@@ -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);   }
folly/folly/container/span.h view
@@ -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>
folly/folly/container/tape.h view
@@ -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));   } 
folly/folly/container/test/TrackingTypes.h view
@@ -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 = ", ";
folly/folly/coro/AsyncGenerator.h view
@@ -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
folly/folly/coro/AsyncPipe.h view
@@ -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()) {
folly/folly/coro/AsyncScope.h view
@@ -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));   }    /**
folly/folly/coro/AsyncStack.h view
@@ -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 {
− folly/folly/coro/AwaitImmediately.h
@@ -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
− folly/folly/coro/AwaitResult.h
@@ -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
+ folly/folly/coro/BasePromise.h view
@@ -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
folly/folly/coro/BlockingWait.h view
@@ -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();       }
folly/folly/coro/Collect-inl.h view
@@ -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>>>
folly/folly/coro/Collect.h view
@@ -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>>&&)
folly/folly/coro/Coroutine.h view
@@ -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
+ folly/folly/coro/Error.h view
@@ -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
folly/folly/coro/FutureUtil.h view
@@ -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)); }
folly/folly/coro/GmockHelpers.h view
@@ -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
folly/folly/coro/GtestHelpers.h view
@@ -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_(                                                  \
− folly/folly/coro/Noexcept.h
@@ -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
+ folly/folly/coro/Nothrow.h view
@@ -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
folly/folly/coro/Promise.h view
@@ -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)
− folly/folly/coro/Ready.h
@@ -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
folly/folly/coro/Result.h view
@@ -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
folly/folly/coro/Retry.h view
@@ -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;     }   } 
folly/folly/coro/RustAdaptors.h view
@@ -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;
folly/folly/coro/ScopeExit.h view
@@ -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 {
folly/folly/coro/SharedLock.h view
@@ -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,
folly/folly/coro/SharedMutex.h view
@@ -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>
folly/folly/coro/SharedPromise.h view
@@ -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>
folly/folly/coro/SmallUnboundedQueue.h view
@@ -73,7 +73,7 @@         });         co_await c.baton;         if (cancelled) {-          co_yield co_cancelled;+          co_yield co_stopped_may_throw;         }       }       buffer_ = queue_.getMessages();
folly/folly/coro/Synchronized.h view
@@ -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
folly/folly/coro/Task.h view
@@ -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))}; }
folly/folly/coro/TaskWrapper.h view
@@ -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
folly/folly/coro/Timeout-inl.h view
@@ -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)); }
folly/folly/coro/Timeout.h view
@@ -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,
folly/folly/coro/UnboundedQueue.h view
@@ -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}; }; 
+ folly/folly/coro/ValueOrError.h view
@@ -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
+ folly/folly/coro/ValueOrFatal.h view
@@ -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
folly/folly/coro/ViaIfAsync.h view
@@ -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 
folly/folly/coro/WithAsyncStack.h view
@@ -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;+      }     }   } 
folly/folly/coro/WithCancellation.h view
@@ -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
folly/folly/coro/detail/PickTaskWrapper.h view
@@ -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
folly/folly/coro/safe/AsyncClosure.h view
@@ -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{}>
+ folly/folly/coro/safe/BindCaptures.h view
@@ -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
folly/folly/coro/safe/Captures.h view
@@ -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 
folly/folly/coro/safe/NowTask.h view
@@ -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 
− folly/folly/coro/safe/SafeAlias.h
@@ -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
folly/folly/coro/safe/SafeTask.h view
@@ -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
folly/folly/coro/safe/detail/AsyncClosure.h view
@@ -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<
− folly/folly/coro/safe/detail/AsyncClosureBindings.h
@@ -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
+ folly/folly/coro/safe/detail/BindAsyncClosure.h view
@@ -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
folly/folly/debugging/exception_tracer/ExceptionCounterLib.cpp view
@@ -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 
folly/folly/debugging/exception_tracer/ExceptionCounterLib.h view
@@ -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.
+ folly/folly/debugging/exception_tracer/ExceptionCounterLibStaticRegistration.cpp view
@@ -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
folly/folly/debugging/exception_tracer/ExceptionStackTraceLib.cpp view
@@ -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 
folly/folly/debugging/exception_tracer/ExceptionTracer.cpp view
@@ -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");       }     }   };
folly/folly/debugging/exception_tracer/ExceptionTracerLib.cpp view
@@ -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
folly/folly/debugging/exception_tracer/ExceptionTracerLib.h view
@@ -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
folly/folly/debugging/exception_tracer/SmartExceptionStackTraceHooks.cpp view
@@ -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&) {   } }
folly/folly/debugging/exception_tracer/SmartExceptionTracer.cpp view
@@ -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
folly/folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h view
@@ -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(); 
folly/folly/debugging/symbolizer/Dwarf.cpp view
@@ -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 {
folly/folly/debugging/symbolizer/Dwarf.h view
@@ -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 {
folly/folly/debugging/symbolizer/DwarfImpl.cpp view
@@ -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.
folly/folly/debugging/symbolizer/DwarfImpl.h view
@@ -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 {
folly/folly/debugging/symbolizer/DwarfLineNumberVM.h view
@@ -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 {
folly/folly/debugging/symbolizer/DwarfUtil.cpp view
@@ -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.
folly/folly/debugging/symbolizer/DwarfUtil.h view
@@ -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 {
folly/folly/debugging/symbolizer/Elf-inl.h view
@@ -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
folly/folly/debugging/symbolizer/Elf.cpp view
@@ -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 
folly/folly/debugging/symbolizer/Elf.h view
@@ -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] = {};
folly/folly/debugging/symbolizer/ElfCache.h view
@@ -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;
folly/folly/debugging/symbolizer/SignalHandler.cpp view
@@ -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;
folly/folly/debugging/symbolizer/SignalHandler.h view
@@ -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);  /**
folly/folly/debugging/symbolizer/StackTrace.cpp view
@@ -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; }
folly/folly/debugging/symbolizer/SymbolizePrinter.h view
@@ -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;
folly/folly/debugging/symbolizer/Symbolizer.cpp view
@@ -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() {
folly/folly/debugging/symbolizer/Symbolizer.h view
@@ -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 
folly/folly/debugging/symbolizer/detail/Debug.cpp view
@@ -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
folly/folly/detail/AsyncTrace.cpp view
@@ -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 {}
folly/folly/detail/AtomicUnorderedMapUtils.h view
@@ -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
folly/folly/detail/FileUtilDetail.h view
@@ -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);
folly/folly/detail/GroupVarintDetail.h view
@@ -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) }; }; 
folly/folly/detail/IPAddress.cpp view
@@ -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
folly/folly/detail/IPAddressSource.h view
@@ -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);
folly/folly/detail/MPMCPipelineDetail.h view
@@ -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; 
folly/folly/detail/MemoryIdler.cpp view
@@ -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
folly/folly/detail/MemoryIdler.h view
@@ -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 /
folly/folly/detail/PerfScoped.cpp view
@@ -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_;
folly/folly/detail/PolyDetail.h view
@@ -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; }
− folly/folly/detail/SplitStringSimd.cpp
@@ -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
folly/folly/detail/SplitStringSimd.h view
@@ -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
folly/folly/detail/SplitStringSimdImpl.h view
@@ -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);   } }; 
folly/folly/detail/ThreadLocalDetail.cpp view
@@ -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);
folly/folly/detail/ThreadLocalDetail.h view
@@ -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,
folly/folly/detail/base64_detail/Base64Api.h view
@@ -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
folly/folly/detail/base64_detail/Base64Common.h view
@@ -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 {
folly/folly/detail/base64_detail/Base64Constants.h view
@@ -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
folly/folly/detail/base64_detail/Base64Scalar.h view
@@ -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
folly/folly/detail/tuple.h view
@@ -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 {
folly/folly/dynamic-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/json/dynamic-inl.h>+#include <folly/json/dynamic-inl.h> // @shim
folly/folly/dynamic.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/json/dynamic.h>+#include <folly/json/dynamic.h> // @shim
folly/folly/executors/CPUThreadPoolExecutor.cpp view
@@ -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;     }   } }
folly/folly/executors/CPUThreadPoolExecutor.h view
@@ -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
folly/folly/executors/Codel.cpp view
@@ -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),
folly/folly/executors/EDFThreadPoolExecutor.cpp view
@@ -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) {
folly/folly/executors/EDFThreadPoolExecutor.h view
@@ -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
folly/folly/executors/ExecutionObserver.h view
@@ -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; 
folly/folly/executors/FunctionScheduler.cpp view
@@ -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);     }   } }
folly/folly/executors/FunctionScheduler.h view
@@ -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.
folly/folly/executors/GlobalExecutor.cpp view
@@ -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")));
folly/folly/executors/IOObjectCache.h view
@@ -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)) {}
folly/folly/executors/IOThreadPoolExecutor.cpp view
@@ -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);   } }
folly/folly/executors/ManualExecutor.h view
@@ -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:
folly/folly/executors/ScheduledExecutor.h view
@@ -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; 
folly/folly/executors/SerialExecutor.h view
@@ -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
folly/folly/executors/SoftRealTimeExecutor.h view
@@ -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);
folly/folly/executors/StrandExecutor.cpp view
@@ -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) {
+ folly/folly/executors/StripedEDFThreadPoolExecutor.cpp view
@@ -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
+ folly/folly/executors/StripedEDFThreadPoolExecutor.h view
@@ -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
folly/folly/executors/ThreadPoolExecutor.cpp view
@@ -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();
folly/folly/executors/ThreadPoolExecutor.h view
@@ -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)) {
folly/folly/executors/task_queue/BlockingQueue.h view
@@ -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; }
folly/folly/executors/task_queue/LifoSemMPMCQueue.h view
@@ -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_;
folly/folly/executors/task_queue/PriorityLifoSemMPMCQueue.h view
@@ -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_;
folly/folly/executors/task_queue/PriorityUnboundedBlockingQueue.h view
@@ -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) {
+ folly/folly/executors/task_queue/StripedPriorityUnboundedBlockingQueue.h view
@@ -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
folly/folly/executors/task_queue/UnboundedBlockingQueue.h view
@@ -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_;
folly/folly/executors/thread_factory/InitThreadFactory.h view
@@ -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(
folly/folly/experimental/EventCount.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/synchronization/EventCount.h>+#include <folly/synchronization/EventCount.h> // @shim
folly/folly/experimental/FlatCombiningPriorityQueue.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/concurrency/container/FlatCombiningPriorityQueue.h>+#include <folly/concurrency/container/FlatCombiningPriorityQueue.h> // @shim
folly/folly/experimental/FunctionScheduler.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/executors/FunctionScheduler.h>+#include <folly/executors/FunctionScheduler.h> // @shim
− folly/folly/experimental/TestUtil.h
@@ -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>
folly/folly/experimental/ThreadedRepeatingFunctionRunner.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/executors/ThreadedRepeatingFunctionRunner.h>+#include <folly/executors/ThreadedRepeatingFunctionRunner.h> // @shim
folly/folly/experimental/channels/Channel-fwd.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Channel-fwd.h>+#include <folly/channels/Channel-fwd.h> // @shim
folly/folly/experimental/channels/Channel-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Channel-inl.h>+#include <folly/channels/Channel-inl.h> // @shim
folly/folly/experimental/channels/Channel.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Channel.h>+#include <folly/channels/Channel.h> // @shim
folly/folly/experimental/channels/ChannelCallbackHandle.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/ChannelCallbackHandle.h>+#include <folly/channels/ChannelCallbackHandle.h> // @shim
folly/folly/experimental/channels/ChannelProcessor-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/ChannelProcessor-inl.h>+#include <folly/channels/ChannelProcessor-inl.h> // @shim
folly/folly/experimental/channels/ChannelProcessor.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/ChannelProcessor.h>+#include <folly/channels/ChannelProcessor.h> // @shim
folly/folly/experimental/channels/ConsumeChannel-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/ConsumeChannel-inl.h>+#include <folly/channels/ConsumeChannel-inl.h> // @shim
folly/folly/experimental/channels/ConsumeChannel.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/ConsumeChannel.h>+#include <folly/channels/ConsumeChannel.h> // @shim
folly/folly/experimental/channels/FanoutChannel-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/FanoutChannel-inl.h>+#include <folly/channels/FanoutChannel-inl.h> // @shim
folly/folly/experimental/channels/FanoutChannel.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/FanoutChannel.h>+#include <folly/channels/FanoutChannel.h> // @shim
folly/folly/experimental/channels/FanoutSender-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/FanoutSender-inl.h>+#include <folly/channels/FanoutSender-inl.h> // @shim
folly/folly/experimental/channels/FanoutSender.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/FanoutSender.h>+#include <folly/channels/FanoutSender.h> // @shim
folly/folly/experimental/channels/MaxConcurrentRateLimiter.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/MaxConcurrentRateLimiter.h>+#include <folly/channels/MaxConcurrentRateLimiter.h> // @shim
folly/folly/experimental/channels/Merge-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Merge-inl.h>+#include <folly/channels/Merge-inl.h> // @shim
folly/folly/experimental/channels/Merge.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Merge.h>+#include <folly/channels/Merge.h> // @shim
folly/folly/experimental/channels/MergeChannel-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/MergeChannel-inl.h>+#include <folly/channels/MergeChannel-inl.h> // @shim
folly/folly/experimental/channels/MergeChannel.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/MergeChannel.h>+#include <folly/channels/MergeChannel.h> // @shim
folly/folly/experimental/channels/MultiplexChannel-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/MultiplexChannel-inl.h>+#include <folly/channels/MultiplexChannel-inl.h> // @shim
folly/folly/experimental/channels/MultiplexChannel.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/MultiplexChannel.h>+#include <folly/channels/MultiplexChannel.h> // @shim
folly/folly/experimental/channels/OnClosedException.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/OnClosedException.h>+#include <folly/channels/OnClosedException.h> // @shim
folly/folly/experimental/channels/Producer-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Producer-inl.h>+#include <folly/channels/Producer-inl.h> // @shim
folly/folly/experimental/channels/Producer.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Producer.h>+#include <folly/channels/Producer.h> // @shim
folly/folly/experimental/channels/ProxyChannel-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/ProxyChannel-inl.h>+#include <folly/channels/ProxyChannel-inl.h> // @shim
folly/folly/experimental/channels/ProxyChannel.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/ProxyChannel.h>+#include <folly/channels/ProxyChannel.h> // @shim
folly/folly/experimental/channels/RateLimiter.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/RateLimiter.h>+#include <folly/channels/RateLimiter.h> // @shim
folly/folly/experimental/channels/Transform-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Transform-inl.h>+#include <folly/channels/Transform-inl.h> // @shim
folly/folly/experimental/channels/Transform.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/Transform.h>+#include <folly/channels/Transform.h> // @shim
folly/folly/experimental/channels/detail/AtomicQueue.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/detail/AtomicQueue.h>+#include <folly/channels/detail/AtomicQueue.h> // @shim
folly/folly/experimental/channels/detail/ChannelBridge.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/detail/ChannelBridge.h>+#include <folly/channels/detail/ChannelBridge.h> // @shim
folly/folly/experimental/channels/detail/FunctionTraits.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/detail/FunctionTraits.h>+#include <folly/channels/detail/FunctionTraits.h> // @shim
folly/folly/experimental/channels/detail/IntrusivePtr.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/detail/IntrusivePtr.h>+#include <folly/channels/detail/IntrusivePtr.h> // @shim
folly/folly/experimental/channels/detail/MultiplexerTraits.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/detail/MultiplexerTraits.h>+#include <folly/channels/detail/MultiplexerTraits.h> // @shim
folly/folly/experimental/channels/detail/PointerVariant.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/detail/PointerVariant.h>+#include <folly/channels/detail/PointerVariant.h> // @shim
folly/folly/experimental/channels/detail/Utility.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/channels/detail/Utility.h>+#include <folly/channels/detail/Utility.h> // @shim
folly/folly/experimental/coro/AsyncGenerator.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/AsyncGenerator.h>+#include <folly/coro/AsyncGenerator.h> // @shim
folly/folly/experimental/coro/AsyncPipe.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/AsyncPipe.h>+#include <folly/coro/AsyncPipe.h> // @shim
folly/folly/experimental/coro/AsyncScope.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/AsyncScope.h>+#include <folly/coro/AsyncScope.h> // @shim
folly/folly/experimental/coro/AsyncStack.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/AsyncStack.h>+#include <folly/coro/AsyncStack.h> // @shim
folly/folly/experimental/coro/AutoCleanup-fwd.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/AutoCleanup-fwd.h>+#include <folly/coro/AutoCleanup-fwd.h> // @shim
folly/folly/experimental/coro/AutoCleanup.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/AutoCleanup.h>+#include <folly/coro/AutoCleanup.h> // @shim
folly/folly/experimental/coro/Baton.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Baton.h>+#include <folly/coro/Baton.h> // @shim
folly/folly/experimental/coro/BlockingWait.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/BlockingWait.h>+#include <folly/coro/BlockingWait.h> // @shim
folly/folly/experimental/coro/BoundedQueue.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/BoundedQueue.h>+#include <folly/coro/BoundedQueue.h> // @shim
folly/folly/experimental/coro/Cleanup.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Cleanup.h>+#include <folly/coro/Cleanup.h> // @shim
folly/folly/experimental/coro/Collect-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Collect-inl.h>+#include <folly/coro/Collect-inl.h> // @shim
folly/folly/experimental/coro/Collect.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Collect.h>+#include <folly/coro/Collect.h> // @shim
folly/folly/experimental/coro/Concat-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Concat-inl.h>+#include <folly/coro/Concat-inl.h> // @shim
folly/folly/experimental/coro/Concat.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Concat.h>+#include <folly/coro/Concat.h> // @shim
folly/folly/experimental/coro/Coroutine.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Coroutine.h>+#include <folly/coro/Coroutine.h> // @shim
folly/folly/experimental/coro/CurrentExecutor.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/CurrentExecutor.h>+#include <folly/coro/CurrentExecutor.h> // @shim
folly/folly/experimental/coro/DetachOnCancel.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/DetachOnCancel.h>+#include <folly/coro/DetachOnCancel.h> // @shim
folly/folly/experimental/coro/Filter-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Filter-inl.h>+#include <folly/coro/Filter-inl.h> // @shim
folly/folly/experimental/coro/Filter.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Filter.h>+#include <folly/coro/Filter.h> // @shim
folly/folly/experimental/coro/FutureUtil.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/FutureUtil.h>+#include <folly/coro/FutureUtil.h> // @shim
folly/folly/experimental/coro/Generator.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Generator.h>+#include <folly/coro/Generator.h> // @shim
folly/folly/experimental/coro/GmockHelpers.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/GmockHelpers.h>+#include <folly/coro/GmockHelpers.h> // @shim
folly/folly/experimental/coro/GtestHelpers.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/GtestHelpers.h>+#include <folly/coro/GtestHelpers.h> // @shim
folly/folly/experimental/coro/Invoke.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Invoke.h>+#include <folly/coro/Invoke.h> // @shim
folly/folly/experimental/coro/Merge-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Merge-inl.h>+#include <folly/coro/Merge-inl.h> // @shim
folly/folly/experimental/coro/Merge.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Merge.h>+#include <folly/coro/Merge.h> // @shim
folly/folly/experimental/coro/Mutex.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Mutex.h>+#include <folly/coro/Mutex.h> // @shim
folly/folly/experimental/coro/Promise.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Promise.h>+#include <folly/coro/Promise.h> // @shim
folly/folly/experimental/coro/Result.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Result.h>+#include <folly/coro/Result.h> // @shim
folly/folly/experimental/coro/Retry.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Retry.h>+#include <folly/coro/Retry.h> // @shim
folly/folly/experimental/coro/RustAdaptors.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/RustAdaptors.h>+#include <folly/coro/RustAdaptors.h> // @shim
folly/folly/experimental/coro/ScopeExit.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/ScopeExit.h>+#include <folly/coro/ScopeExit.h> // @shim
folly/folly/experimental/coro/SharedLock.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/SharedLock.h>+#include <folly/coro/SharedLock.h> // @shim
folly/folly/experimental/coro/SharedMutex.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/SharedMutex.h>+#include <folly/coro/SharedMutex.h> // @shim
folly/folly/experimental/coro/SharedPromise.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/SharedPromise.h>+#include <folly/coro/SharedPromise.h> // @shim
folly/folly/experimental/coro/Sleep-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Sleep-inl.h>+#include <folly/coro/Sleep-inl.h> // @shim
folly/folly/experimental/coro/Sleep.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Sleep.h>+#include <folly/coro/Sleep.h> // @shim
folly/folly/experimental/coro/SmallUnboundedQueue.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/SmallUnboundedQueue.h>+#include <folly/coro/SmallUnboundedQueue.h> // @shim
folly/folly/experimental/coro/Task.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Task.h>+#include <folly/coro/Task.h> // @shim
folly/folly/experimental/coro/TimedWait.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/TimedWait.h>+#include <folly/coro/TimedWait.h> // @shim
folly/folly/experimental/coro/Timeout-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Timeout-inl.h>+#include <folly/coro/Timeout-inl.h> // @shim
folly/folly/experimental/coro/Timeout.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Timeout.h>+#include <folly/coro/Timeout.h> // @shim
folly/folly/experimental/coro/Traits.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Traits.h>+#include <folly/coro/Traits.h> // @shim
folly/folly/experimental/coro/Transform-inl.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Transform-inl.h>+#include <folly/coro/Transform-inl.h> // @shim
folly/folly/experimental/coro/Transform.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/Transform.h>+#include <folly/coro/Transform.h> // @shim
folly/folly/experimental/coro/UnboundedQueue.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/UnboundedQueue.h>+#include <folly/coro/UnboundedQueue.h> // @shim
folly/folly/experimental/coro/ViaIfAsync.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/ViaIfAsync.h>+#include <folly/coro/ViaIfAsync.h> // @shim
folly/folly/experimental/coro/WithAsyncStack.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/WithAsyncStack.h>+#include <folly/coro/WithAsyncStack.h> // @shim
folly/folly/experimental/coro/WithCancellation.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/WithCancellation.h>+#include <folly/coro/WithCancellation.h> // @shim
folly/folly/experimental/coro/detail/Barrier.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/detail/Barrier.h>+#include <folly/coro/detail/Barrier.h> // @shim
folly/folly/experimental/coro/detail/BarrierTask.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/detail/BarrierTask.h>+#include <folly/coro/detail/BarrierTask.h> // @shim
folly/folly/experimental/coro/detail/CurrentAsyncFrame.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/detail/CurrentAsyncFrame.h>+#include <folly/coro/detail/CurrentAsyncFrame.h> // @shim
folly/folly/experimental/coro/detail/Helpers.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/detail/Helpers.h>+#include <folly/coro/detail/Helpers.h> // @shim
folly/folly/experimental/coro/detail/InlineTask.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/detail/InlineTask.h>+#include <folly/coro/detail/InlineTask.h> // @shim
folly/folly/experimental/coro/detail/Malloc.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/detail/Malloc.h>+#include <folly/coro/detail/Malloc.h> // @shim
folly/folly/experimental/coro/detail/ManualLifetime.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/detail/ManualLifetime.h>+#include <folly/coro/detail/ManualLifetime.h> // @shim
folly/folly/experimental/coro/detail/Traits.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/coro/detail/Traits.h>+#include <folly/coro/detail/Traits.h> // @shim
folly/folly/experimental/crypto/Blake2xb.h view
@@ -16,4 +16,4 @@  #pragma once -#include <folly/crypto/Blake2xb.h>+#include <folly/crypto/Blake2xb.h> // @shim
folly/folly/experimental/crypto/LtHash.h view
@@ -16,4 +16,4 @@  #pragma once -#include <folly/crypto/LtHash.h>+#include <folly/crypto/LtHash.h> // @shim
folly/folly/experimental/exception_tracer/ExceptionAbi.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/debugging/exception_tracer/ExceptionAbi.h>+#include <folly/debugging/exception_tracer/ExceptionAbi.h> // @shim
folly/folly/experimental/exception_tracer/ExceptionCounterLib.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/debugging/exception_tracer/ExceptionCounterLib.h>+#include <folly/debugging/exception_tracer/ExceptionCounterLib.h> // @shim
folly/folly/experimental/exception_tracer/ExceptionTracer.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/debugging/exception_tracer/ExceptionTracer.h>+#include <folly/debugging/exception_tracer/ExceptionTracer.h> // @shim
folly/folly/experimental/exception_tracer/ExceptionTracerLib.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>+#include <folly/debugging/exception_tracer/ExceptionTracerLib.h> // @shim
folly/folly/experimental/exception_tracer/SmartExceptionTracer.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/debugging/exception_tracer/SmartExceptionTracer.h>+#include <folly/debugging/exception_tracer/SmartExceptionTracer.h> // @shim
folly/folly/experimental/exception_tracer/SmartExceptionTracerSingleton.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h>+#include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h> // @shim
folly/folly/experimental/exception_tracer/StackTrace.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/debugging/exception_tracer/StackTrace.h>+#include <folly/debugging/exception_tracer/StackTrace.h> // @shim
folly/folly/experimental/flat_combining/FlatCombining.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/synchronization/FlatCombining.h>+#include <folly/synchronization/FlatCombining.h> // @shim
folly/folly/experimental/io/AsyncBase.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/AsyncBase.h>+#include <folly/io/async/AsyncBase.h> // @shim
folly/folly/experimental/io/AsyncIO.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/AsyncIO.h>+#include <folly/io/async/AsyncIO.h> // @shim
folly/folly/experimental/io/AsyncIoUringSocket.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/AsyncIoUringSocket.h>+#include <folly/io/async/AsyncIoUringSocket.h> // @shim
folly/folly/experimental/io/AsyncIoUringSocketFactory.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/AsyncIoUringSocketFactory.h>+#include <folly/io/async/AsyncIoUringSocketFactory.h> // @shim
folly/folly/experimental/io/Epoll.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/Epoll.h>+#include <folly/io/async/Epoll.h> // @shim
folly/folly/experimental/io/EpollBackend.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/EpollBackend.h>+#include <folly/io/async/EpollBackend.h> // @shim
folly/folly/experimental/io/EventBasePoller.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/EventBasePoller.h>+#include <folly/io/async/EventBasePoller.h> // @shim
folly/folly/experimental/io/FsUtil.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/FsUtil.h>+#include <folly/io/FsUtil.h> // @shim
folly/folly/experimental/io/HugePages.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/HugePages.h>+#include <folly/io/HugePages.h> // @shim
folly/folly/experimental/io/IoUring.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/IoUring.h>+#include <folly/io/async/IoUring.h> // @shim
folly/folly/experimental/io/IoUringBackend.h view
@@ -14,4 +14,4 @@  * limitations under the License.  */ -#include <folly/io/async/IoUringBackend.h>+#include <folly/io/async/IoUringBackend.h> // @shim
folly/folly/experimental/io/IoUringBase.h view

file too large to diff

folly/folly/experimental/io/IoUringEvent.h view

file too large to diff

folly/folly/experimental/io/IoUringEventBaseLocal.h view

file too large to diff

folly/folly/experimental/io/IoUringProvidedBufferRing.h view

file too large to diff

folly/folly/experimental/io/Liburing.h view

file too large to diff

folly/folly/experimental/io/MuxIOThreadPoolExecutor.h view

file too large to diff

folly/folly/experimental/io/SimpleAsyncIO.h view

file too large to diff

folly/folly/experimental/observer/CoreCachedObserver.h view

file too large to diff

folly/folly/experimental/observer/HazptrObserver.h view

file too large to diff

folly/folly/experimental/observer/Observable-inl.h view

file too large to diff

folly/folly/experimental/observer/Observable.h view

file too large to diff

folly/folly/experimental/observer/Observer-inl.h view

file too large to diff

folly/folly/experimental/observer/Observer-pre.h view

file too large to diff

folly/folly/experimental/observer/Observer.h view

file too large to diff

folly/folly/experimental/observer/ReadMostlyTLObserver.h view

file too large to diff

folly/folly/experimental/observer/SimpleObservable-inl.h view

file too large to diff

folly/folly/experimental/observer/SimpleObservable.h view

file too large to diff

folly/folly/experimental/observer/WithJitter-inl.h view

file too large to diff

folly/folly/experimental/observer/WithJitter.h view

file too large to diff

folly/folly/experimental/observer/detail/Core.h view

file too large to diff

folly/folly/experimental/observer/detail/GraphCycleDetector.h view

file too large to diff

folly/folly/experimental/observer/detail/ObserverManager.h view

file too large to diff

folly/folly/experimental/settings/Immutables.h view

file too large to diff

folly/folly/experimental/settings/Settings.h view

file too large to diff

folly/folly/experimental/settings/Types.h view

file too large to diff

folly/folly/experimental/settings/detail/SettingsImpl.h view

file too large to diff

folly/folly/experimental/symbolizer/Dwarf.h view

file too large to diff

folly/folly/experimental/symbolizer/DwarfImpl.h view

file too large to diff

folly/folly/experimental/symbolizer/DwarfLineNumberVM.h view

file too large to diff

folly/folly/experimental/symbolizer/DwarfSection.h view

file too large to diff

folly/folly/experimental/symbolizer/DwarfUtil.h view

file too large to diff

folly/folly/experimental/symbolizer/Elf-inl.h view

file too large to diff

folly/folly/experimental/symbolizer/Elf.h view

file too large to diff

folly/folly/experimental/symbolizer/ElfCache.h view

file too large to diff

folly/folly/experimental/symbolizer/LineReader.h view

file too large to diff

folly/folly/experimental/symbolizer/SignalHandler.h view

file too large to diff

folly/folly/experimental/symbolizer/StackTrace.h view

file too large to diff

folly/folly/experimental/symbolizer/SymbolizePrinter.h view

file too large to diff

folly/folly/experimental/symbolizer/SymbolizedFrame.h view

file too large to diff

folly/folly/experimental/symbolizer/Symbolizer.h view

file too large to diff

folly/folly/experimental/symbolizer/detail/Debug.h view

file too large to diff

− folly/folly/ext/buck2/test_ext.cpp

file too large to diff

folly/folly/ext/test_ext.h view

file too large to diff

folly/folly/external/fast-crc32/avx512_crc32c_v8s3x4.h view

file too large to diff

folly/folly/external/fast-crc32/sse_crc32c_v8s3x3.h view

file too large to diff

folly/folly/fibers/AddTasks.h view

file too large to diff

folly/folly/fibers/AtomicBatchDispatcher-inl.h view

file too large to diff

folly/folly/fibers/Baton.h view

file too large to diff

folly/folly/fibers/Fiber.cpp view

file too large to diff

folly/folly/fibers/FiberManager.cpp view

file too large to diff

folly/folly/fibers/FiberManagerInternal.h view

file too large to diff

folly/folly/fibers/ForEach-inl.h view

file too large to diff

folly/folly/fibers/GenericBaton.h view

file too large to diff

folly/folly/fibers/GuardPageAllocator.cpp view

file too large to diff

folly/folly/fibers/GuardPageAllocator.h view

file too large to diff

folly/folly/fibers/LoopController.h view

file too large to diff

folly/folly/fibers/Promise-inl.h view

file too large to diff

folly/folly/fibers/Promise.h view

file too large to diff

folly/folly/fibers/Semaphore.cpp view

file too large to diff

folly/folly/fibers/SemaphoreBase.cpp view

file too large to diff

folly/folly/fibers/TimedMutex-inl.h view

file too large to diff

folly/folly/fibers/TimedMutex.h view

file too large to diff

folly/folly/fibers/WhenN-inl.h view

file too large to diff

folly/folly/fibers/async/Async.h view

file too large to diff

folly/folly/fibers/async/Collect-inl.h view

file too large to diff

folly/folly/fibers/traits.h view

file too large to diff

folly/folly/functional/ApplyTuple.h view

file too large to diff

folly/folly/functional/Invoke.h view

file too large to diff

folly/folly/functional/Partial.h view

file too large to diff

folly/folly/functional/protocol.h view

file too large to diff

folly/folly/functional/traits.h view

file too large to diff

folly/folly/futures/Barrier.h view

file too large to diff

folly/folly/futures/Future-inl.h view

file too large to diff

folly/folly/futures/Future.cpp view

file too large to diff

folly/folly/futures/Future.h view

file too large to diff

folly/folly/futures/HeapTimekeeper.cpp view

file too large to diff

folly/folly/futures/HeapTimekeeper.h view

file too large to diff

folly/folly/futures/Portability.h view

file too large to diff

folly/folly/futures/Retrying.h view

file too large to diff

folly/folly/futures/WTCallback.h view

file too large to diff

folly/folly/futures/detail/Core.h view

file too large to diff

folly/folly/gen/Base-inl.h view

file too large to diff

folly/folly/gen/Base.h view

file too large to diff

folly/folly/gen/Combine-inl.h view

file too large to diff

folly/folly/gen/ParallelMap-inl.h view

file too large to diff

folly/folly/hash/Checksum.cpp view

file too large to diff

folly/folly/hash/Checksum.h view

file too large to diff

+ folly/folly/hash/FnvHash.h view

file too large to diff

folly/folly/hash/Hash.h view

file too large to diff

+ folly/folly/hash/HsiehHash.h view

file too large to diff

folly/folly/hash/SpookyHashV1.cpp view

file too large to diff

folly/folly/hash/SpookyHashV2.cpp view

file too large to diff

+ folly/folly/hash/UniqueHashKey.cpp view

file too large to diff

+ folly/folly/hash/UniqueHashKey.h view

file too large to diff

folly/folly/hash/detail/ChecksumDetail.cpp view

file too large to diff

folly/folly/hash/detail/ChecksumDetail.h view

file too large to diff

folly/folly/hash/detail/Crc32CombineDetail.cpp view

file too large to diff

folly/folly/hash/detail/Crc32cDetail.cpp view

file too large to diff

+ folly/folly/hash/detail/RandomSeed.h view

file too large to diff

folly/folly/hash/traits.h view

file too large to diff

folly/folly/init/Init.cpp view

file too large to diff

folly/folly/init/Init.h view

file too large to diff

folly/folly/io/Cursor.h view

file too large to diff

folly/folly/io/FsUtil.cpp view

file too large to diff

folly/folly/io/FsUtil.h view

file too large to diff

folly/folly/io/HugePages.cpp view

file too large to diff

folly/folly/io/HugePages.h view

file too large to diff

folly/folly/io/IOBuf.cpp view

file too large to diff

folly/folly/io/IOBuf.h view

file too large to diff

folly/folly/io/IOBufQueue.cpp view

file too large to diff

folly/folly/io/RecordIO.cpp view

file too large to diff

folly/folly/io/RecordIO.h view

file too large to diff

folly/folly/io/ShutdownSocketSet.cpp view

file too large to diff

folly/folly/io/TypedIOBuf.h view

file too large to diff

folly/folly/io/async/AsyncBase.cpp view

file too large to diff

folly/folly/io/async/AsyncIoUringSocket.cpp view

file too large to diff

folly/folly/io/async/AsyncIoUringSocket.h view

file too large to diff

+ folly/folly/io/async/AsyncIoUringSocketFactory.cpp view

file too large to diff

folly/folly/io/async/AsyncIoUringSocketFactory.h view

file too large to diff

folly/folly/io/async/AsyncPipe.cpp view

file too large to diff

folly/folly/io/async/AsyncSSLSocket.cpp view

file too large to diff

folly/folly/io/async/AsyncSSLSocket.h view

file too large to diff

folly/folly/io/async/AsyncServerSocket.cpp view

file too large to diff

folly/folly/io/async/AsyncServerSocket.h view

file too large to diff

folly/folly/io/async/AsyncSignalHandler.cpp view

file too large to diff

folly/folly/io/async/AsyncSocket.cpp view

file too large to diff

folly/folly/io/async/AsyncSocket.h view

file too large to diff

folly/folly/io/async/AsyncSocketException.h view

file too large to diff

folly/folly/io/async/AsyncSocketTransport.cpp view

file too large to diff

folly/folly/io/async/AsyncSocketTransport.h view

file too large to diff

folly/folly/io/async/AsyncTimeout.h view

file too large to diff

folly/folly/io/async/AsyncTransport.h view

file too large to diff

folly/folly/io/async/AsyncUDPServerSocket.h view

file too large to diff

folly/folly/io/async/CertificateIdentityVerifier.h view

file too large to diff

folly/folly/io/async/DelayedDestructionBase.h view

file too large to diff

folly/folly/io/async/EpollBackend.cpp view

file too large to diff

folly/folly/io/async/EventBase.cpp view

file too large to diff

folly/folly/io/async/EventBase.h view

file too large to diff

folly/folly/io/async/EventBaseAtomicNotificationQueue.h view

file too large to diff

folly/folly/io/async/EventBaseBackendBase.cpp view

file too large to diff

folly/folly/io/async/EventBaseBackendBase.h view

file too large to diff

folly/folly/io/async/EventBaseLocal.cpp view

file too large to diff

folly/folly/io/async/EventBasePoller.cpp view

file too large to diff

folly/folly/io/async/EventBaseThread.cpp view

file too large to diff

folly/folly/io/async/EventHandler.cpp view

file too large to diff

folly/folly/io/async/EventHandler.h view

file too large to diff

folly/folly/io/async/EventUtil.h view

file too large to diff

folly/folly/io/async/HHWheelTimer.cpp view

file too large to diff

folly/folly/io/async/HHWheelTimer.h view

file too large to diff

folly/folly/io/async/IoUring.cpp view

file too large to diff

folly/folly/io/async/IoUringBackend.cpp view

file too large to diff

folly/folly/io/async/IoUringBackend.h view

file too large to diff

folly/folly/io/async/IoUringBase.h view

file too large to diff

+ folly/folly/io/async/IoUringConnect.cpp view

file too large to diff

+ folly/folly/io/async/IoUringConnect.h view

file too large to diff

folly/folly/io/async/IoUringEvent.cpp view

file too large to diff

folly/folly/io/async/IoUringEvent.h view

file too large to diff

folly/folly/io/async/IoUringEventBaseLocal.cpp view

file too large to diff

folly/folly/io/async/IoUringEventBaseLocal.h view

file too large to diff

folly/folly/io/async/IoUringProvidedBufferRing.cpp view

file too large to diff

folly/folly/io/async/IoUringProvidedBufferRing.h view

file too large to diff

+ folly/folly/io/async/IoUringRecv.cpp view

file too large to diff

+ folly/folly/io/async/IoUringRecv.h view

file too large to diff

+ folly/folly/io/async/IoUringSend.cpp view

file too large to diff

+ folly/folly/io/async/IoUringSend.h view

file too large to diff

folly/folly/io/async/IoUringZeroCopyBufferPool.cpp view

file too large to diff

folly/folly/io/async/IoUringZeroCopyBufferPool.h view

file too large to diff

folly/folly/io/async/MuxIOThreadPoolExecutor.cpp view

file too large to diff

folly/folly/io/async/NotificationQueue.h view

file too large to diff

folly/folly/io/async/Request.cpp view

file too large to diff

folly/folly/io/async/Request.h view

file too large to diff

folly/folly/io/async/SSLContext.cpp view

file too large to diff

folly/folly/io/async/STTimerFDTimeoutManager.h view

file too large to diff

folly/folly/io/async/ScopedEventBaseThread.cpp view

file too large to diff

folly/folly/io/async/ScopedEventBaseThread.h view

file too large to diff

folly/folly/io/async/SimpleAsyncIO.cpp view

file too large to diff

folly/folly/io/async/TimeoutManager.h view

file too large to diff

folly/folly/io/async/TimerFD.cpp view

file too large to diff

folly/folly/io/async/TimerFD.h view

file too large to diff

folly/folly/io/async/TimerFDTimeoutManager.h view

file too large to diff

folly/folly/io/async/fdsock/AsyncFdSocket.cpp view

file too large to diff

folly/folly/io/async/observer/AsyncSocketObserverInterface.h view

file too large to diff

folly/folly/io/async/ssl/SSLErrors.cpp view

file too large to diff

folly/folly/io/async/ssl/SSLErrors.h view

file too large to diff

folly/folly/io/async/test/AsyncSSLSocketTest.h view

file too large to diff

folly/folly/io/async/test/AsyncSocketTest.h view

file too large to diff

folly/folly/io/async/test/AsyncSocketTest2.h view

file too large to diff

folly/folly/io/async/test/MockAsyncServerSocket.h view

file too large to diff

folly/folly/io/async/test/MockAsyncSocket.h view

file too large to diff

folly/folly/io/async/test/MockAsyncTransport.h view

file too large to diff

− folly/folly/io/async/test/ScopedBoundPort.cpp

file too large to diff

− folly/folly/io/async/test/SocketPair.cpp

file too large to diff

− folly/folly/io/async/test/TimeUtil.cpp

file too large to diff

folly/folly/io/coro/ServerSocket.cpp view

file too large to diff

folly/folly/io/coro/ServerSocket.h view

file too large to diff

folly/folly/io/coro/TransportCallbackBase.h view

file too large to diff

folly/folly/json.h view

file too large to diff

folly/folly/json/DynamicConverter.h view

file too large to diff

folly/folly/json/DynamicParser-inl.h view

file too large to diff

folly/folly/json/DynamicParser.h view

file too large to diff

folly/folly/json/JSONSchema.cpp view

file too large to diff

folly/folly/json/bser/Dump.cpp view

file too large to diff

folly/folly/json/bser/Load.cpp view

file too large to diff

folly/folly/json/dynamic-inl.h view

file too large to diff

folly/folly/json/dynamic.h view

file too large to diff

folly/folly/json/json.cpp view

file too large to diff

folly/folly/json/json_pointer.cpp view

file too large to diff

folly/folly/json_patch.h view

file too large to diff

folly/folly/json_pointer.h view

file too large to diff

folly/folly/lang/Align.h view

file too large to diff

− folly/folly/lang/Bindings.h

file too large to diff

folly/folly/lang/Bits.h view

file too large to diff

folly/folly/lang/BitsClass.h view

file too large to diff

folly/folly/lang/Exception.cpp view

file too large to diff

folly/folly/lang/Exception.h view

file too large to diff

folly/folly/lang/Hint-inl.h view

file too large to diff

+ folly/folly/lang/MustUseImmediately.h view

file too large to diff

folly/folly/lang/New.h view

file too large to diff

folly/folly/lang/SafeAlias-fwd.h view

file too large to diff

+ folly/folly/lang/SafeAlias.h view

file too large to diff

folly/folly/lang/SafeAssert.cpp view

file too large to diff

folly/folly/lang/SafeAssert.h view

file too large to diff

+ folly/folly/lang/SafeClosure.h view

file too large to diff

+ folly/folly/lang/Strong.h view

file too large to diff

folly/folly/lang/Switch.h view

file too large to diff

folly/folly/lang/ToAscii.h view

file too large to diff

folly/folly/lang/UncaughtExceptions.h view

file too large to diff

+ folly/folly/lang/bind/AsArgument.h view

file too large to diff

+ folly/folly/lang/bind/Bind.h view

file too large to diff

+ folly/folly/lang/bind/Named.h view

file too large to diff

+ folly/folly/lang/bind/NamedToStorage.h view

file too large to diff

+ folly/folly/lang/bind/ToStorage.h view

file too large to diff

+ folly/folly/lang/cstring_view.h view

file too large to diff

− folly/folly/lang/named/Bindings.h

file too large to diff

folly/folly/logging/AutoTimer.h view

file too large to diff

folly/folly/logging/CustomLogFormatter.cpp view

file too large to diff

folly/folly/logging/FileWriterFactory.cpp view

file too large to diff

folly/folly/logging/Init.cpp view

file too large to diff

folly/folly/logging/LogConfig.cpp view

file too large to diff

folly/folly/logging/LogConfigParser.cpp view

file too large to diff

folly/folly/logging/LogLevel.cpp view

file too large to diff

folly/folly/logging/LogStreamProcessor.cpp view

file too large to diff

folly/folly/logging/LogStreamProcessor.h view

file too large to diff

folly/folly/logging/Logger.cpp view

file too large to diff

folly/folly/logging/LoggerDB.cpp view

file too large to diff

folly/folly/logging/ObjectToString.h view

file too large to diff

folly/folly/logging/StandardLogHandlerFactory.cpp view

file too large to diff

folly/folly/logging/StreamHandlerFactory.cpp view

file too large to diff

folly/folly/logging/xlog.cpp view

file too large to diff

folly/folly/logging/xlog.h view

file too large to diff

+ folly/folly/memcpy.S view

file too large to diff

− folly/folly/memcpy_select_aarch64.cpp

file too large to diff

+ folly/folly/memory/IoUringArena.cpp view

file too large to diff

+ folly/folly/memory/IoUringArena.h view

file too large to diff

folly/folly/memory/JemallocNodumpAllocator.cpp view

file too large to diff

+ folly/folly/memory/Malloc.cpp view

file too large to diff

folly/folly/memory/Malloc.h view

file too large to diff

folly/folly/memory/ReentrantAllocator.h view

file too large to diff

folly/folly/memory/UninitializedMemoryHacks.h view

file too large to diff

folly/folly/memory/not_null-inl.h view

file too large to diff

folly/folly/memory/not_null.h view

file too large to diff

− folly/folly/memset_select_aarch64.cpp

file too large to diff

folly/folly/net/NetOps.cpp view

file too large to diff

folly/folly/net/NetOps.h view

file too large to diff

folly/folly/net/NetOpsDispatcher.cpp view

file too large to diff

folly/folly/net/NetOpsDispatcher.h view

file too large to diff

folly/folly/net/TcpInfo.h view

file too large to diff

folly/folly/observer/CoreCachedObserver.h view

file too large to diff

folly/folly/observer/HazptrObserver.h view

file too large to diff

folly/folly/observer/ReadMostlyTLObserver.h view

file too large to diff

folly/folly/observer/WithJitter-inl.h view

file too large to diff

folly/folly/observer/detail/Core.h view

file too large to diff

folly/folly/observer/detail/ObserverManager.cpp view

file too large to diff

folly/folly/portability/Filesystem.cpp view

file too large to diff

folly/folly/portability/Filesystem.h view

file too large to diff

folly/folly/portability/GFlags.h view

file too large to diff

folly/folly/portability/Stdlib.h view

file too large to diff

folly/folly/portability/SysMman.cpp view

file too large to diff

folly/folly/portability/SysMman.h view

file too large to diff

folly/folly/portability/SysSyscall.h view

file too large to diff

folly/folly/portability/SysTypes.h view

file too large to diff

folly/folly/portability/Time.cpp view

file too large to diff

folly/folly/portability/Unistd.h view

file too large to diff

+ folly/folly/portability/openat2.c view

file too large to diff

folly/folly/python/AsyncioExecutor.h view

file too large to diff

+ folly/folly/python/GILAwareManualExecutor.h view

file too large to diff

+ folly/folly/python/ProactorExecutor.h view

file too large to diff

folly/folly/python/Weak.h view

file too large to diff

+ folly/folly/python/fibers.h view

file too large to diff

folly/folly/python/import.h view

file too large to diff

+ folly/folly/python/iobuf_ext.h view

file too large to diff

+ folly/folly/python/request_context.h view

file too large to diff

+ folly/folly/random/hash.h view

file too large to diff

+ folly/folly/random/seed_seq.h view

file too large to diff

folly/folly/random/xoshiro256pp.h view

file too large to diff

+ folly/folly/result/coded_rich_error.h view

file too large to diff

+ folly/folly/result/coro.h view

file too large to diff

+ folly/folly/result/detail/immortal_exception_storage.h view

file too large to diff

+ folly/folly/result/detail/ptr_immortal_exception_storage.h view

file too large to diff

+ folly/folly/result/detail/result_or_unwind.h view

file too large to diff

+ folly/folly/result/detail/result_promise.h view

file too large to diff

+ folly/folly/result/detail/rich_error_common.h view

file too large to diff

+ folly/folly/result/detail/rich_exception_ptr_storage.cpp view

file too large to diff

+ folly/folly/result/detail/rich_exception_ptr_storage.h view

file too large to diff

+ folly/folly/result/epitaph.cpp view

file too large to diff

+ folly/folly/result/epitaph.h view

file too large to diff

+ folly/folly/result/errc_rich_error.cpp view

file too large to diff

+ folly/folly/result/errc_rich_error.h view

file too large to diff

folly/folly/result/gtest_helpers.h view

file too large to diff

+ folly/folly/result/immortal_rich_error.h view

file too large to diff

+ folly/folly/result/nestable_coded_rich_error.h view

file too large to diff

+ folly/folly/result/or_unwind_epitaph.h view

file too large to diff

folly/folly/result/result.cpp view

file too large to diff

folly/folly/result/result.h view

file too large to diff

+ folly/folly/result/rich_error.h view

file too large to diff

+ folly/folly/result/rich_error_base.cpp view

file too large to diff

+ folly/folly/result/rich_error_base.h view

file too large to diff

+ folly/folly/result/rich_error_code.cpp view

file too large to diff

+ folly/folly/result/rich_error_code.h view

file too large to diff

+ folly/folly/result/rich_error_fwd.h view

file too large to diff

+ folly/folly/result/rich_exception_ptr.cpp view

file too large to diff

+ folly/folly/result/rich_exception_ptr.h view

file too large to diff

+ folly/folly/result/rich_msg.h view

file too large to diff

folly/folly/result/try.h view

file too large to diff

+ folly/folly/result/value_only_result.h view

file too large to diff

+ folly/folly/result/value_only_result_coro.h view

file too large to diff

folly/folly/settings/Observer.h view

file too large to diff

folly/folly/settings/Settings.cpp view

file too large to diff

folly/folly/settings/Settings.h view

file too large to diff

folly/folly/settings/Types.h view

file too large to diff

folly/folly/settings/detail/SettingsImpl.h view

file too large to diff

folly/folly/small_vector.h view

file too large to diff

folly/folly/sorted_vector_types.h view

file too large to diff

folly/folly/ssl/OpenSSLCertUtils.cpp view

file too large to diff

folly/folly/ssl/OpenSSLCertUtils.h view

file too large to diff

folly/folly/ssl/OpenSSLHash.cpp view

file too large to diff

folly/folly/ssl/OpenSSLHash.h view

file too large to diff

folly/folly/ssl/OpenSSLKeyUtils.cpp view

file too large to diff

folly/folly/ssl/OpenSSLKeyUtils.h view

file too large to diff

folly/folly/stats/BucketedTimeSeries-inl.h view

file too large to diff

folly/folly/stats/BucketedTimeSeries.h view

file too large to diff

folly/folly/stats/DigestBuilder-inl.h view

file too large to diff

folly/folly/stats/Histogram.h view

file too large to diff

folly/folly/stats/MultiLevelTimeSeries-inl.h view

file too large to diff

folly/folly/stats/MultiLevelTimeSeries.h view

file too large to diff

folly/folly/stats/QuantileEstimator.cpp view

file too large to diff

folly/folly/stats/TimeseriesHistogram.h view

file too large to diff

folly/folly/stats/detail/Bucket.h view

file too large to diff

folly/folly/synchronization/AsymmetricThreadFence.h view

file too large to diff

folly/folly/synchronization/AtomicNotification-inl.h view

file too large to diff

folly/folly/synchronization/AtomicUtil.h view

file too large to diff

folly/folly/synchronization/Baton.h view

file too large to diff

folly/folly/synchronization/CallOnce.h view

file too large to diff

folly/folly/synchronization/DistributedMutex-inl.h view

file too large to diff

folly/folly/synchronization/DistributedMutex.h view

file too large to diff

folly/folly/synchronization/FlatCombining.h view

file too large to diff

folly/folly/synchronization/Hazptr-fwd.h view

file too large to diff

folly/folly/synchronization/Hazptr.h view

file too large to diff

folly/folly/synchronization/HazptrDomain.cpp view

file too large to diff

folly/folly/synchronization/HazptrDomain.h view

file too large to diff

folly/folly/synchronization/HazptrHolder.h view

file too large to diff

folly/folly/synchronization/HazptrObj.h view

file too large to diff

folly/folly/synchronization/HazptrRec.h view

file too large to diff

folly/folly/synchronization/HazptrThreadPoolExecutor.cpp view

file too large to diff

folly/folly/synchronization/Lock.h view

file too large to diff

folly/folly/synchronization/MicroSpinLock.h view

file too large to diff

folly/folly/synchronization/RWSpinLock.h view

file too large to diff

folly/folly/synchronization/Rcu.h view

file too large to diff

folly/folly/synchronization/RelaxedAtomic.h view

file too large to diff

folly/folly/synchronization/SaturatingSemaphore.h view

file too large to diff

+ folly/folly/synchronization/StripedThrottledLifoSem.cpp view

file too large to diff

+ folly/folly/synchronization/StripedThrottledLifoSem.h view

file too large to diff

folly/folly/synchronization/ThrottledLifoSem.h view

file too large to diff

− folly/folly/synchronization/WaitOptions.cpp

file too large to diff

folly/folly/synchronization/detail/AtomicUtils.h view

file too large to diff

− folly/folly/synchronization/detail/Hardware.cpp

file too large to diff

− folly/folly/synchronization/detail/Hardware.h

file too large to diff

folly/folly/synchronization/detail/Sleeper.cpp view

file too large to diff

folly/folly/system/EnvUtil.cpp view

file too large to diff

folly/folly/system/EnvUtil.h view

file too large to diff

folly/folly/system/HardwareConcurrency.cpp view

file too large to diff

folly/folly/system/HardwareConcurrency.h view

file too large to diff

folly/folly/system/MemoryMapping.cpp view

file too large to diff

folly/folly/system/MemoryMapping.h view

file too large to diff

+ folly/folly/system/arch/x86.h view

file too large to diff

folly/folly/test/DeterministicSchedule.h view

file too large to diff

folly/folly/testing/TestUtil.cpp view

file too large to diff

folly/folly/testing/TestUtil.h view

file too large to diff

folly/folly/tracing/AsyncStack.cpp view

file too large to diff

folly/folly/tracing/AsyncStack.h view

file too large to diff