loader.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <nodelet/loader.h>
00031 #include <nodelet/nodelet.h>
00032 #include <nodelet/detail/callback_queue.h>
00033 #include <nodelet/detail/callback_queue_manager.h>
00034 #include <pluginlib/class_loader.h>
00035 #include <bondcpp/bond.h>
00036 
00037 #include <ros/ros.h>
00038 #include <ros/callback_queue.h>
00039 #include <nodelet/NodeletLoad.h>
00040 #include <nodelet/NodeletList.h>
00041 #include <nodelet/NodeletUnload.h>
00042 
00043 #include <boost/ptr_container/ptr_map.hpp>
00044 #include <boost/utility.hpp>
00045 
00046 /*
00047 Between Loader, Nodelet, CallbackQueue and CallbackQueueManager, who owns what?
00048 
00049 Loader contains the CallbackQueueManager. Loader and CallbackQueueManager share
00050 ownership of the CallbackQueues. Loader is primary owner of the Nodelets, but
00051 each CallbackQueue has weak ownership of its associated Nodelet.
00052 
00053 Loader ensures that the CallbackQueues associated with a Nodelet outlive that
00054 Nodelet. CallbackQueueManager ensures that CallbackQueues continue to survive as
00055 long as they have pending callbacks. 
00056 
00057 CallbackQueue holds a weak_ptr to its associated Nodelet, which it attempts to
00058 lock before invoking any callback. So any lingering callbacks processed after a
00059 Nodelet is destroyed are safely discarded, and then the CallbackQueue expires.
00060 
00061 When Loader unloads a Nodelet, it calls CallbackQueueManager::removeQueue() for
00062 the associated CallbackQueues, which prevents them from adding any more callbacks
00063 to CallbackQueueManager. Otherwise a Nodelet that is continuously executing
00064 callbacks might persist in a "zombie" state after being unloaded.
00065  */
00066 
00067 namespace nodelet
00068 {
00069 
00070 typedef boost::shared_ptr<Nodelet> NodeletPtr;
00071 
00073 class LoaderROS
00074 {
00075 public:
00076   LoaderROS(Loader* parent, const ros::NodeHandle& nh)
00077   : parent_(parent)
00078   , nh_(nh)
00079   , bond_spinner_(1, &bond_callback_queue_)
00080   {
00081     load_server_ = nh_.advertiseService("load_nodelet", &LoaderROS::serviceLoad, this);
00082     unload_server_ = nh_.advertiseService("unload_nodelet", &LoaderROS::serviceUnload, this);
00083     list_server_ = nh_.advertiseService("list", &LoaderROS::serviceList, this);
00084 
00085     bond_spinner_.start();
00086   }
00087 
00088 private:
00089   bool serviceLoad(nodelet::NodeletLoad::Request &req,
00090                    nodelet::NodeletLoad::Response &res)
00091   {
00092     // build map
00093     M_string remappings;
00094     if (req.remap_source_args.size() != req.remap_target_args.size())
00095     {
00096       ROS_ERROR("Bad remapppings provided, target and source of different length");
00097     }
00098     else
00099     {
00100       for (size_t i = 0; i < req.remap_source_args.size(); ++i)
00101       {
00102         remappings[ros::names::resolve(req.remap_source_args[i])] = ros::names::resolve(req.remap_target_args[i]);
00103         ROS_DEBUG("%s:%s\n", ros::names::resolve(req.remap_source_args[i]).c_str(), remappings[ros::names::resolve(req.remap_source_args[i])].c_str());
00104       }
00105     }
00106 
00107     res.success = parent_->load(req.name, req.type, remappings, req.my_argv);
00108 
00109     // If requested, create bond to sister process
00110     if (res.success && !req.bond_id.empty())
00111     {
00112       bond::Bond* bond = new bond::Bond(nh_.getNamespace() + "/bond", req.bond_id);
00113       bond_map_.insert(req.name, bond);
00114       bond->setCallbackQueue(&bond_callback_queue_);
00115       bond->setBrokenCallback(boost::bind(&LoaderROS::unload, this, req.name));
00116       bond->start();
00117     }
00118     return res.success;
00119   }
00120 
00121   bool serviceUnload(nodelet::NodeletUnload::Request &req,
00122                      nodelet::NodeletUnload::Response &res)
00123   {
00124     res.success = unload(req.name);
00125     return res.success;
00126   }
00127 
00128   bool unload(const std::string& name)
00129   {
00130     bool success = parent_->unload(name);
00131     if (!success)
00132     {
00133       ROS_ERROR("Failed to find nodelet with name '%s' to unload.", name.c_str());
00134       return success;
00135     }
00136 
00137     // Break the bond, if there is one
00138     bond_map_.erase(name);
00139 
00140     return success;
00141   }
00142 
00143   bool serviceList(nodelet::NodeletList::Request &req,
00144                    nodelet::NodeletList::Response &res)
00145   {
00146     res.nodelets = parent_->listLoadedNodelets();
00147     return true;
00148   }
00149 
00150   Loader* parent_;
00151   ros::NodeHandle nh_;
00152   ros::ServiceServer load_server_;
00153   ros::ServiceServer unload_server_;
00154   ros::ServiceServer list_server_;
00155   
00156   ros::CallbackQueue bond_callback_queue_;
00157   ros::AsyncSpinner bond_spinner_;
00158   typedef boost::ptr_map<std::string, bond::Bond> M_stringToBond;
00159   M_stringToBond bond_map_;
00160 };
00161 
00162 // Owns a Nodelet and its callback queues
00163 struct ManagedNodelet : boost::noncopyable
00164 {
00165   detail::CallbackQueuePtr st_queue;
00166   detail::CallbackQueuePtr mt_queue;
00167   NodeletPtr nodelet; // destroyed before the queues
00168   detail::CallbackQueueManager* callback_manager;
00169 
00171   ManagedNodelet(const NodeletPtr& nodelet, detail::CallbackQueueManager* cqm)
00172     : st_queue(new detail::CallbackQueue(cqm, nodelet))
00173     , mt_queue(new detail::CallbackQueue(cqm, nodelet))
00174     , nodelet(nodelet)
00175     , callback_manager(cqm)
00176   {
00177     // NOTE: Can't do this in CallbackQueue constructor because the shared_ptr to
00178     // it doesn't exist then.
00179     callback_manager->addQueue(st_queue, false);
00180     callback_manager->addQueue(mt_queue, true);
00181   }
00182 
00183   ~ManagedNodelet()
00184   {
00185     callback_manager->removeQueue(st_queue);
00186     callback_manager->removeQueue(mt_queue);
00187   }
00188 };
00189 
00190 struct Loader::Impl
00191 {
00192   boost::shared_ptr<LoaderROS> services_;
00193 
00194   boost::function<boost::shared_ptr<Nodelet> (const std::string& lookup_name)> create_instance_;
00195   boost::function<void ()> refresh_classes_;
00196   boost::shared_ptr<detail::CallbackQueueManager> callback_manager_; // Must outlive nodelets_
00197 
00198   typedef boost::ptr_map<std::string, ManagedNodelet> M_stringToNodelet;
00199   M_stringToNodelet nodelets_; 
00200 
00201   Impl()
00202   {
00203     // Under normal circumstances, we use pluginlib to load any registered nodelet
00204     typedef pluginlib::ClassLoader<Nodelet> Loader;
00205     boost::shared_ptr<Loader> loader(new Loader("nodelet", "nodelet::Nodelet"));
00206     // create_instance_ is self-contained; it owns a copy of the loader shared_ptr
00207     create_instance_ = boost::bind(&Loader::createInstance, loader, _1);
00208     refresh_classes_ = boost::bind(&Loader::refreshDeclaredClasses, loader);
00209   }
00210 
00211   Impl(const boost::function<boost::shared_ptr<Nodelet> (const std::string& lookup_name)>& create_instance)
00212     : create_instance_(create_instance)
00213   {
00214   }
00215 
00216   void advertiseRosApi(Loader* parent, const ros::NodeHandle& server_nh)
00217   {
00218     int num_threads_param;
00219     server_nh.param("num_worker_threads", num_threads_param, 0);
00220     callback_manager_.reset(new detail::CallbackQueueManager(num_threads_param));
00221     ROS_INFO("Initializing nodelet with %d worker threads.", (int)callback_manager_->getNumWorkerThreads());
00222   
00223     services_.reset(new LoaderROS(parent, server_nh));
00224   }
00225 };
00226 
00228 Loader::Loader(bool provide_ros_api)
00229   : impl_(new Impl)
00230 {
00231   if (provide_ros_api)
00232     impl_->advertiseRosApi(this, ros::NodeHandle("~"));
00233   else
00234     impl_->callback_manager_.reset(new detail::CallbackQueueManager);
00235 }
00236 
00237 Loader::Loader(const ros::NodeHandle& server_nh)
00238   : impl_(new Impl)
00239 {
00240   impl_->advertiseRosApi(this, server_nh);
00241 }
00242 
00243 Loader::Loader(const boost::function<boost::shared_ptr<Nodelet> (const std::string& lookup_name)>& create_instance)
00244   : impl_(new Impl(create_instance))
00245 {
00246   impl_->callback_manager_.reset(new detail::CallbackQueueManager);
00247 }
00248 
00249 Loader::~Loader()
00250 {
00251 }
00252 
00253 bool Loader::load(const std::string &name, const std::string& type, const ros::M_string& remappings,
00254                   const std::vector<std::string> & my_argv)
00255 {
00256   boost::mutex::scoped_lock lock(lock_);
00257   if (impl_->nodelets_.count(name) > 0)
00258   {
00259     ROS_ERROR("Cannot load nodelet %s for one exists with that name already", name.c_str());
00260     return false;
00261   }
00262 
00263   NodeletPtr p;
00264   try
00265   {
00266     p = impl_->create_instance_(type);
00267   }
00268   catch (std::runtime_error& e)
00269   {
00270     try
00271     {
00272       impl_->refresh_classes_();
00273       p = impl_->create_instance_(type);
00274     }
00275     catch (std::runtime_error& e)
00276     {
00277       ROS_ERROR("Failed to load nodelet [%s] of type [%s]: %s", name.c_str(), type.c_str(), e.what());
00278       return false;
00279     }
00280   }
00281 
00282   if (!p)
00283   {
00284     return false;
00285   }
00286   ROS_DEBUG("Done loading nodelet %s", name.c_str());
00287 
00288   ManagedNodelet* mn = new ManagedNodelet(p, impl_->callback_manager_.get());
00289   impl_->nodelets_.insert(const_cast<std::string&>(name), mn); // mn now owned by boost::ptr_map
00290   try {
00291     p->init(name, remappings, my_argv, mn->st_queue.get(), mn->mt_queue.get());
00293 
00294     ROS_DEBUG("Done initing nodelet %s", name.c_str());
00295     return true;
00296   } catch(...) {
00297     Impl::M_stringToNodelet::iterator it = impl_->nodelets_.find(name);
00298     if (it != impl_->nodelets_.end())
00299     {
00300       impl_->nodelets_.erase(it);
00301       ROS_DEBUG ("Failed to initialize nodelet %s", name.c_str ());
00302       return (false);
00303     }
00304   }
00305 }
00306 
00307 bool Loader::unload (const std::string & name)
00308 {
00309   boost::mutex::scoped_lock lock (lock_);
00310   Impl::M_stringToNodelet::iterator it = impl_->nodelets_.find(name);
00311   if (it != impl_->nodelets_.end())
00312   {
00313     impl_->nodelets_.erase(it);
00314     ROS_DEBUG ("Done unloading nodelet %s", name.c_str ());
00315     return (true);
00316   }
00317 
00318   return (false);
00319 }
00320 
00321 bool Loader::clear ()
00322 {
00323   boost::mutex::scoped_lock lock(lock_);
00324   impl_->nodelets_.clear();
00325   return true;
00326 };
00327 
00328 std::vector<std::string> Loader::listLoadedNodelets()
00329 {
00330   boost::mutex::scoped_lock lock (lock_);
00331   std::vector<std::string> output;
00332   Impl::M_stringToNodelet::iterator it = impl_->nodelets_.begin();
00333   for (; it != impl_->nodelets_.end(); ++it)
00334   {
00335     output.push_back(it->first);
00336   }
00337   return output;
00338 }
00339 
00340 } // namespace nodelet
00341 


nodelet
Author(s): Tully Foote, Radu Bogdan Rusu
autogenerated on Wed Aug 26 2015 14:56:45