folly/folly/executors/StrandExecutor.h

/*
 * 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