1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
/*
* Copyright (C) 2018 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef IGNITION_PLUGIN_FACTORY_HH_
#define IGNITION_PLUGIN_FACTORY_HH_
#include <chrono>
#include <functional>
#include <memory>
#include <tuple>
#include <ignition/plugin/EnablePluginFromThis.hh>
namespace ignition
{
namespace plugin
{
/// \brief This class provides a unary operator for safely deleting pointers
/// to plugin factory products with the type `Interface`. If it gets passed
/// an `Interface` pointer that is not pointing to a factory plugin product,
/// then this just performs a normal delete.
template <typename Interface> class ProductDeleter;
/// \brief ProductPtr is a derivative of std::unique_ptr that can safely
/// manage the products that come out of a plugin factory. It is strongly
/// recommended that factory products use a ProductPtr to manage the
/// lifecycle of a factory product.
///
/// If you MUST release a factory product pointer, then it should at least
/// be passed to a ProductDeleter to be deleted at the end of its lifecycle.
/// If it is not possible to delete the product with a ProductDeleter, then
/// you will need to periodically call CleanupLostProducts() or else you
/// will experience memory leaks, and your factory plugin libraries will
/// never get unloaded.
template <typename Interface>
using ProductPtr =
std::unique_ptr<Interface, ProductDeleter<Interface>>;
/// \brief The Factory class defines a plugin factory that can be used by
/// the Loader class to produce products that implement an interface.
///
/// To define the inputs and outputs of a factory, set the template
/// arguments, for example:
///
/// \code
/// using MyFactory = Factory<InterfaceClass, InputType1, InputType2>;
/// \endcode
///
/// defines a factory that can produce a `std::unique_ptr<InterfaceClass>`
/// given arguments of `InputType1` and `InputType2`.
///
/// To register a factory, use the `IGNITION_ADD_FACTORY` macro, e.g.:
///
/// \code
/// IGNITION_ADD_FACTORY(ImplementedClass, MyFactory)
/// \endcode
///
/// where `ImplementedClass` is the name of the class that your plugin
/// library has used to implement `InterfaceClass`.
template <typename Interface, typename... Args>
class Factory : public EnablePluginFromThis
{
public: using ProductPtrType = ProductPtr<Interface>;
/// \brief This function is called by Loader to construct the class that
/// implements the InterfacePtr interface.
/// \param[in] _args
/// The arguments as defined by the template parameters.
/// \return an RAII-managed reference to the interface type as defined by
/// the template parameters.
public: ProductPtrType Construct(Args&&... _args);
/// \internal \brief This function gets implemented by Producing<Product>
/// to manufacture the product instance.
/// \param[in] _args
/// The arguments as defined by the template parameters
/// \return a raw pointer to the product
private: virtual Interface *ImplConstruct(Args&&... _args) = 0;
/// \private This nested class is used to implement the plugin factory.
/// It is not intended for external use.
public: template <typename Product>
class Producing;
};
/// \brief Call this function to cleanup the Factories of any Products which
/// were not managed by a ProductPtr or deleted by a ProductDeleter (in
/// other words, the Product was released from its ProductPtr and then its
/// lifecycle was managed by a framework that does not know it has special
/// deletion requirements).
///
/// If you never call the .release() function on a ProductPtr, then you will
/// never need to call this function.
///
/// \warning Note that this function should be called ONLY while there are
/// no Products that are actively destructing, or else there is a miniscule
/// probability of causing a segmentation fault. This is never an issue in a
/// single-threaded application.
///
/// \warning If you have a multi-threaded application where you have
/// absolutely no control over the lifecycle of the products, and you cannot
/// reliably predict a safe window in which you know that no products are
/// being actively deleted, then you can specify a short safety wait to the
/// cleanup call. If any products were being deleted while this function is
/// called, this wait can give them time to fully exit their destructors
/// before we unload their libraries.
///
/// \note For some applications, it might not be important if there are tiny
/// memory leaks or if plugin libraries remain loaded until the application
/// exits. For those applications, it is okay to not bother calling this
/// function at all.
///
/// \param[in] _safetyWait
/// For multi-threaded applications, this short waiting window gives time
/// for products that are currently being deleted to exit their
/// destructors before we unload their libraries. If you can reliably
/// predict a window of time in which no products are actively being
/// destructed (or if you have a single-threaded application), then it is
/// okay to set this waiting time to 0. Note that any threads which are
/// about to destruct a product will be blocked until this wait is over.
void IGNITION_PLUGIN_VISIBLE CleanupLostProducts(
const std::chrono::nanoseconds &_safetyWait =
std::chrono::nanoseconds(5));
/// \brief Get the number of lost products that have currently accumulated
/// since the last time CleanupLostProducts() was called (or since the
/// program began, if CleanupLostProducts() has not been called yet).
std::size_t IGNITION_PLUGIN_VISIBLE LostProductCount();
}
}
#include <ignition/plugin/detail/Factory.hh>
#endif
|