1 /*
2  * Copyright (C) 2017 Open Source Robotics Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 
19 #include "ignition/common/Plugin.hh"
20 #include "ignition/common/PluginInfo.hh"
21 #include "ignition/common/Console.hh"
22 #include "PluginUtils.hh"
23 
24 namespace ignition
25 {
26   namespace common
27   {
28     class PluginPrivate
29     {
30       /// \brief Clear this PluginPrivate without invaliding any map entry
31       /// iterators.
Clear()32       public: void Clear()
33       {
34         this->loadedInstancePtr.reset();
35 
36         // Dev note (MXG): We must NOT call clear() on the InterfaceMap or
37         // remove ANY of the map entries, because that would potentially
38         // invalidate all of the iterators that are pointing to map entries.
39         // This would break any specialized plugins that provide instant access
40         // to specialized interfaces. Instead, we simply overwrite the map
41         // entries with a nullptr.
42         for (auto &entry : this->interfaces)
43           entry.second = nullptr;
44       }
45 
46       /// \brief Initialize this PluginPrivate using some PluginInfo instance
47       /// \param[in] _info Information describing the plugin to initialize
Initialize(const PluginInfo * _info)48       public: void Initialize(const PluginInfo *_info)
49       {
50         this->Clear();
51 
52         if (!_info)
53           return;
54 
55         this->loadedInstancePtr =
56             std::shared_ptr<void>(_info->factory(), _info->deleter);
57 
58         if (this->loadedInstancePtr)
59         {
60           for (const auto &entry : _info->interfaces)
61           {
62             // entry.first:  name of the interface
63             // entry.second: function which casts the pluginInstance pointer to
64             //               the correct location of the interface within the
65             //               plugin
66             this->interfaces[entry.first] =
67                 entry.second(this->loadedInstancePtr.get());
68           }
69         }
70       }
71 
72       /// \brief Initialize this PluginPrivate using another instance
73       /// \param[in] _other Another instance of a PluginPrivate object
Initialize(const PluginPrivate * _other)74       public: void Initialize(const PluginPrivate *_other)
75       {
76         this->Clear();
77 
78         if (!_other)
79         {
80           ignerr << "Received a nullptr _other in the constructor "
81                  << "which uses `const PluginPrivate*`. This should "
82                  << "not be possible! Please report this bug."
83                  << std::endl;
84           assert(false);
85           return;
86         }
87 
88         this->loadedInstancePtr = _other->loadedInstancePtr;
89 
90         if (this->loadedInstancePtr)
91         {
92           for (const auto &entry : _other->interfaces)
93           {
94             // entry.first:  name of the interface
95             // entry.second: pointer to the location of that interface within
96             //               the plugin instance
97             this->interfaces[entry.first] = entry.second;
98           }
99         }
100       }
101 
102       /// \brief Map from interface names to their locations within the plugin
103       /// instance
104       //
105       // Dev Note (MXG): We use std::map here instead of std::unordered_map
106       // because iterators to a std::map are not invalidated by the insertion
107       // operation (whereas all iterators to a std::unordered_map are
108       // potentially invalidated each time an insertion is performed on the
109       // std::unordered_map). Holding onto valid iterators allows us to do
110       // optimizations with template magic to provide direct access to
111       // interfaces whose availability we can anticipate at run time.
112       //
113       // It is also worth noting that ordered vs unordered lookup time is very
114       // similar on sets where the strings have relatively small lengths (5-20
115       // characters) and a relatively small number of entries in the set (5-20
116       // entries). Those conditions match our expected use case here. In fact,
117       // ordered lookup can sometimes outperform unordered in these conditions.
118       public: Plugin::InterfaceMap interfaces;
119 
120       /// \brief shared_ptr which manages the lifecycle of the plugin instance.
121       std::shared_ptr<void> loadedInstancePtr;
122     };
123 
124     //////////////////////////////////////////////////
HasInterface(const std::string & _interfaceName) const125     bool Plugin::HasInterface(
126         const std::string &_interfaceName) const
127     {
128       const std::string interfaceName = NormalizeName(_interfaceName);
129       return (this->dataPtr->interfaces.count(interfaceName) != 0);
130     }
131 
132     //////////////////////////////////////////////////
Plugin()133     Plugin::Plugin()
134       : dataPtr(new PluginPrivate)
135     {
136       // Do nothing
137     }
138 
139     //////////////////////////////////////////////////
PrivateGetInterface(const std::string & _interfaceName) const140     void *Plugin::PrivateGetInterface(
141         const std::string &_interfaceName) const
142     {
143       const std::string interfaceName = NormalizeName(_interfaceName);
144       const auto &it = this->dataPtr->interfaces.find(interfaceName);
145       if (this->dataPtr->interfaces.end() == it)
146         return nullptr;
147 
148       return it->second;
149     }
150 
151     //////////////////////////////////////////////////
PrivateCopyPluginInstance(const Plugin & _other) const152     void Plugin::PrivateCopyPluginInstance(const Plugin &_other) const
153     {
154       this->dataPtr->Initialize(_other.dataPtr.get());
155     }
156 
157     //////////////////////////////////////////////////
PrivateSetPluginInstance(const PluginInfo * _info) const158     void Plugin::PrivateSetPluginInstance(const PluginInfo *_info) const
159     {
160       this->dataPtr->Initialize(_info);
161     }
162 
163     //////////////////////////////////////////////////
PrivateGetInstancePtr() const164     const std::shared_ptr<void> &Plugin::PrivateGetInstancePtr() const
165     {
166       return this->dataPtr->loadedInstancePtr;
167     }
168 
169     //////////////////////////////////////////////////
PrivateGetOrCreateIterator(const std::string & _interfaceName)170     Plugin::InterfaceMap::iterator Plugin::PrivateGetOrCreateIterator(
171         const std::string &_interfaceName)
172     {
173       // We want to use the insert function here to avoid accidentally
174       // overwriting a value which might exist at the desired map key.
175       return this->dataPtr->interfaces.insert(
176             std::make_pair(NormalizeName(_interfaceName), nullptr)).first;
177     }
178 
179     //////////////////////////////////////////////////
~Plugin()180     Plugin::~Plugin()
181     {
182       // Do nothing. We need this definition to be in a source file so that
183       // the destructor of PluginPrivate is visible to std::unique_ptr.
184     }
185   }
186 }
187