folly/folly/synchronization/detail/InlineFunctionRef.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 <new>

#include <folly/Function.h>
#include <folly/Traits.h>
#include <folly/Utility.h>
#include <folly/functional/Invoke.h>

namespace folly {
namespace detail {

/**
 * InlineFunctionRef is similar to folly::FunctionRef but has the additional
 * benefit of being able to store the function it was instantiated with inline
 * in a buffer of the given capacity.  Inline storage is only used if the
 * function object and a pointer (for type-erasure) are small enough to fit in
 * the templated size.  If there is not enough in-situ capacity for the
 * callable, this just stores a reference to the function object like
 * FunctionRef.
 *
 * This helps give a perf boost in the case where the data gets separated from
 * the point of invocation.  If, for example, at the point of invocation, the
 * InlineFunctionRef object is not cached, a remote memory/cache read might be
 * required to invoke the original callable.  Customizable inline storage
 * helps tune storage so we can store a type-erased callable with better
 * performance and locality.  A real-life example of this might be a
 * folly::FunctionRef with a function pointer.  The folly::FunctionRef would
 * point to the function pointer object in a remote location.  This causes a
 * double-indirection at the point of invocation, and if that memory is dirty,
 * or not cached, it would cause additional cache misses.  On the other hand
 * with InlineFunctionRef, inline storage would store the value of the
 * function pointer, avoiding the need to do a remote lookup to fetch the
 * value of the function pointer.
 *
 * To prevent misuse, InlineFunctionRef disallows construction from an lvalue
 * callable.  This is to prevent usage where a user relies on the callable's
 * state after invocation through InlineFunctionRef.  This has the potential
 * to copy the callable into inline storage when the callable is small, so we
 * might not use the same function when invoking, but rather a copy of it.
 *
 * Also note that InlineFunctionRef will always invoke the const qualified
 * version of the call operator for any callable that is passed.  Regardless
 * of whether it has a non-const version.  This is done to enforce the logical
 * constraint of function state being immutable.
 *
 * This class is always trivially-copyable (and therefore
 * trivially-destructible), making it suitable for use in a union without
 * requiring manual destruction.
 */
template <typename FunctionType, std::size_t Size>
class InlineFunctionRef;

InlineFunctionRef<ReturnType (Args...), Size>;

} // namespace detail
} // namespace folly