/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <atomic> #include <memory> #include <folly/Optional.h> #include <folly/concurrency/UnboundedQueue.h> #include <folly/executors/SerializedExecutor.h> #include <folly/io/async/Request.h> namespace folly { class StrandExecutor; // StrandExecutor / StrandContext // // A StrandExecutor, like a SerialExecutor, serialises execution of work added // to it, while deferring the actual execution of this work to another // Executor. // // However, unlike the SerialExecutor, a StrandExecutor allows multiple // StrandExecutors, each potentially delegating to a different parent Executor, // to share the same serialising queue. // // This can be used in cases where you want to execute work on a // caller-provided execution context, but want to make sure that work is // serialised with respect to other work that might be scheduled on other // underlying Executors. // // e.g. Using StrandExecutor with folly::coro::Task to enforce that only // a single thread accesses the object at a time, while still allowing // other other methods to execute while the current one is suspended // // class MyObject { // public: // // folly::coro::Task<T> someMethod() { // auto currentEx = co_await folly::coro::co_current_executor; // auto strandEx = folly::StrandExecutor::create(strand_, currentEx); // co_return co_await someMethodImpl().scheduleOn(strandEx); // } // // private: // folly::coro::Task<T> someMethodImpl() { // // Safe to access otherState_ without further synchronisation. // modify(otherState_); // // // Other instances of someMethod() calls will be able to run // // while this coroutine is suspended waiting for an RPC call // // to complete. // co_await getClient()->co_someRpcCall(); // // // When that call resumes, this coroutine is once again // // executing with mutual exclusion. // modify(otherState_); // } // // std::shared_ptr<StrandContext> strand_{StrandContext::create()}; // X otherState_; // }; // class StrandContext : public std::enable_shared_from_this<StrandContext> { … }; class StrandExecutor final : public SerializedExecutor { … }; } // namespace folly