Urbi SDK Remote for C++  2.7.5
uobject-common.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009-2012, Gostai S.A.S.
00003  *
00004  * This software is provided "as is" without warranty of any kind,
00005  * either expressed or implied, including but not limited to the
00006  * implied warranties of fitness for a particular purpose.
00007  *
00008  * See the LICENSE file for more information.
00009  */
00010 
00011 #include <cstdarg>
00012 #include <typeinfo>
00013 
00014 #include <libport/cstdio>
00015 #include <libport/debug.hh>
00016 
00017 #include <boost/thread.hpp>
00018 
00019 #include <libport/containers.hh>
00020 #include <libport/foreach.hh>
00021 #include <libport/utime.hh>
00022 
00023 #include <urbi/uobject.hh>
00024 #include <urbi/ustarter.hh>
00025 #include <urbi/ucontext-factory.hh>
00026 
00027 GD_CATEGORY(Urbi.UObject);
00028 
00029 namespace urbi
00030 {
00031   namespace impl
00032   {
00033     std::vector<std::string> listModules()
00034     {
00035       std::vector<std::string> res;
00036       foreach(baseURBIStarter* s, baseURBIStarter::list())
00037       {
00038         res.push_back(s->name);
00039       }
00040       return res;
00041     }
00042   }
00043 
00044   static void noop(impl::UContextImpl*)
00045   {
00046   }
00047 
00048   static boost::thread_specific_ptr<impl::UContextImpl> current_context(&noop);
00049   static impl::UContextImpl* default_context = 0;
00050   impl::UContextImpl* getCurrentContext()
00051   {
00052     impl::UContextImpl* res = current_context.get();
00053     return res ? res : default_context;
00054   }
00055 
00056   void setCurrentContext(impl::UContextImpl* impl)
00057   {
00058     if (!default_context)
00059       default_context = impl;
00060     current_context.reset(impl);
00061   }
00062 
00063 
00064   /*-------------------.
00065   | UGenericCallback.  |
00066   `-------------------*/
00067 
00068   UGenericCallback::UGenericCallback(UObject& owner,
00069                                      UVar* target,
00070                                      const std::string& type,
00071                                      const std::string& name,
00072                                      int size,
00073                                      impl::UContextImpl* ctx)
00074     : UContext(ctx)
00075     , nbparam(size)
00076     , objname(&owner?owner.__name:"dummy")
00077     , type(type)
00078     , name(name)
00079     , target(target)
00080     , owner(owner)
00081     , synchronous_(true)
00082   {
00083     if (target)
00084       target->check();
00085     impl_ = ctx_->getGenericCallbackImpl();
00086     impl_->initialize(this, target?target->owned:false);
00087   }
00088 
00089   UGenericCallback::UGenericCallback(UObject& owner,
00090                                      UVar* target,
00091                                      const std::string& type,
00092                                      const std::string& name,
00093                                      impl::UContextImpl* ctx)
00094     : UContext(ctx)
00095     , objname(&owner?owner.__name:"dummy")
00096     , type(type)
00097     , name(name)
00098     , target(target)
00099     , owner(owner)
00100     , synchronous_(true)
00101   {
00102     if (target)
00103       target->check();
00104     impl_ = ctx_->getGenericCallbackImpl();
00105     impl_->initialize(this);
00106   }
00107 
00108   UGenericCallback::~UGenericCallback()
00109   {
00110     impl_->clear();
00111     delete impl_;
00112   }
00113 
00114   void
00115   UGenericCallback::registerCallback()
00116   {
00117     impl_->registerCallback();
00118   }
00119 
00120   bool
00121   UGenericCallback::isSynchronous() const
00122   {
00123     return synchronous_;
00124   }
00125 
00126   void
00127   UGenericCallback::syncEval(UList& params, OnDone onDone)
00128   {
00129     UValue res;
00130     try {
00131       res = __evalcall(params);
00132       if (onDone)
00133         onDone(res, 0);
00134     }
00135     catch(const std::exception& e)
00136     {
00137       if (onDone)
00138         onDone(res, &e);
00139     }
00140     catch(...)
00141     {
00142       if (onDone)
00143       {
00144         std::runtime_error e("invalid exception");
00145         onDone(res, &e);
00146       }
00147     }
00148   }
00149 
00150   void
00151   UGenericCallback::eval(UList& params, OnDone onDone)
00152   {
00153     if (synchronous_)
00154     {
00155       syncEval(params, onDone);
00156     }
00157     else
00158     {
00159       libport::ThreadPool::rTaskHandle h
00160        = threadPool().queueTask(
00161                              boost::function0<void>(
00162         boost::bind(&UGenericCallback::syncEval, this, params, onDone)),
00163         taskLock);
00164        GD_FINFO_TRACE("Queued async op: with lock %s: %s", taskLock.get(),
00165                       h->getState());
00166       if (h->getState() == libport::ThreadPool::TaskHandle::DROPPED)
00167       {
00168         UValue res;
00169         onDone(res, 0);
00170       }
00171     }
00172   }
00173 
00174   /* Note: to implement the LOCK_MODULE mode, we must delegate search of the
00175    * TaskLock to use to a function within the calling module.
00176    * That is why we take the TaskLock here and we do not find it ourselve.
00177    */
00178   void
00179   UGenericCallback::setAsync(libport::ThreadPool::rTaskLock l)
00180   {
00181     synchronous_ = false;
00182     taskLock = l;
00183   }
00184 
00185   libport::ThreadPool&
00186   UGenericCallback::threadPool()
00187   {
00188     static libport::ThreadPool tp;
00189     return tp;
00190   }
00191 
00192   void
00193   setThreadLimit(size_t nThreads)
00194   {
00195     UGenericCallback::threadPool().resize(nThreads);
00196   }
00197 
00198   /*----------.
00199   | UObject.  |
00200   `----------*/
00201 
00202   UObject::UObject(impl::UContextImpl* impl)
00203    : UContext(impl)
00204    , derived(false)
00205    , autogroup(false)
00206    , remote(true)
00207    , cloner(0)
00208    , impl_(ctx_->getObjectImpl())
00209    , taskLock_(new libport::ThreadPool::TaskLock)
00210  {
00211    impl_->initialize(this);
00212     objecthub = 0;
00213   }
00214 
00215   UObject::UObject(int, impl::UContextImpl* impl)
00216     : UContext(impl)
00217     , __name("_dummy")
00218     , classname("_dummy")
00219     , derived(false)
00220     , autogroup(false)
00221     , remote(true)
00222     , cloner(0)
00223     , impl_(ctx_->getObjectImpl())
00224     , taskLock_(new libport::ThreadPool::TaskLock)
00225   {
00226     impl_->initialize(this);
00227     objecthub = 0;
00228   }
00229 
00231   UObject::UObject(const std::string& s, impl::UContextImpl* impl)
00232     : UContext(impl)
00233     , __name(s)
00234     , classname(s)
00235     , derived(false)
00236     , autogroup(false)
00237     , remote(true)
00238     , cloner(0)
00239     , impl_(ctx_->getObjectImpl())
00240     , taskLock_(new libport::ThreadPool::TaskLock)
00241   {
00242     impl_->initialize(this);
00243     objecthub = 0;
00244     autogroup = false;
00245     // Do not replace this call to init by a `, load(s, "load")' as
00246     // both result in "var <__name>.load = 1", which is not valid
00247     // until the two above lines actually create <__name>.
00248     load.init(__name, "load");
00249     // default
00250     load = 1;
00251   }
00252 
00253   void
00254   UObject::addAutoGroup()
00255   {
00256     UJoinGroup(classname + "s");
00257   }
00258 
00259   void
00260   UObject::UAutoGroup()
00261   {
00262     autogroup = true;
00263   }
00264 
00266   UObject::~UObject()
00267   {
00268     clean();
00269     delete impl_;
00270   }
00271 
00272   void
00273   UObject::UJoinGroup(const std::string& gpname)
00274   {
00275     std::string groupregister = "addgroup " + gpname +" { "+__name+"};";
00276     send(groupregister);
00277   }
00278 
00279   libport::ThreadPool::rTaskLock
00280   UObject::getClassTaskLock()
00281   {
00282     throw std::runtime_error("You must redefine getClassTaskLock to use"
00283                              " asynchronous calls with LOCK_CLASS locking");
00284   }
00285 
00286 
00287   /*---------------.
00288   | UContextImpl.  |
00289   `---------------*/
00290   namespace impl
00291   {
00292 # define GENERIC_TRY(Desc, Code)                                \
00293     do {                                                        \
00294       std::string estr_;                                        \
00295       try                                                       \
00296       {                                                         \
00297         Code;                                                   \
00298       }                                                         \
00299       catch(const std::exception& e)                            \
00300       {                                                         \
00301         estr_ = e.what();                                       \
00302       }                                                         \
00303       catch(...)                                                \
00304       {                                                         \
00305         estr_ = "unknown exception";                            \
00306       }                                                         \
00307       if (!estr_.empty())                                       \
00308         GD_SERROR("Exception " << Desc << ": " << estr_);       \
00309     } while(0)
00310 
00311 
00312     void
00313     UContextImpl::init()
00314     {
00315       setCurrentContext(this);
00316       foreach (baseURBIStarterHub* s, baseURBIStarterHub::list())
00317       {
00318         if (!libport::mhas(initialized, s))
00319         {
00320           GD_FINFO_TRACE("initializing UObject hub: %s", s->name);
00321           GENERIC_TRY("Instanciating hub" << s,
00322                       newUObjectHubClass(s);
00323                       initialized.insert(s);
00324                       );
00325         }
00326       }
00327       foreach (baseURBIStarter* s, baseURBIStarter::list())
00328       {
00329         if (!libport::mhas(initialized, s))
00330         {
00331           GD_FINFO_TRACE("initializing UObject: %s", s->name);
00332           GENERIC_TRY("Instanciating object" << s,
00333                       newUObjectClass(s);
00334                       initialized.insert(s);
00335                       );
00336         }
00337       }
00338     }
00339 #undef GENERIC_TRY
00340 
00341     bool
00342     UContextImpl::bind(const std::string& n, std::string rename)
00343     {
00344       foreach(baseURBIStarter* s, baseURBIStarter::list())
00345       {
00346         if (s->name == n)
00347         {
00348           s->instanciate(this, rename);
00349           return true;
00350         }
00351       }
00352       return false;
00353     }
00354 
00355     void
00356     UContextImpl::registerObject(UObject*o)
00357     {
00358       objects[o->__name] = o;
00359       const std::type_info& ti = typeid(*o);
00360       if (!o->cloner)
00361       {
00362         // Object was instanciated from C++ and has no cloner, try to find one.
00363         foreach(objects_type::value_type& v, objects)
00364         {
00365           GD_FINFO_TRACE("    Scanning %s (%s)",  v.second->__name,
00366                          typeid(*v.second).name());
00367           if (typeid(*v.second) == ti && o != v.second)
00368           {
00369             GD_FINFO_TRACE("Found parent of %s: %s", o->__name,
00370                            v.second->__name);
00371             o->cloner = v.second->cloner;
00372             return;
00373           }
00374         }
00375         GD_FINFO_TRACE("No parent fonud for %s", o->__name);
00376       }
00377     }
00378 
00379     void
00380     UContextImpl::registerHub(UObjectHub*u)
00381     {
00382       hubs[u->get_name()] = u;
00383     }
00384 
00385     UObjectHub*
00386     UContextImpl::getUObjectHub(const std::string& n)
00387     {
00388       return libport::find0(hubs, n);
00389     }
00390 
00391     UObject*
00392     UContextImpl::getUObject(const std::string& n)
00393     {
00394       return libport::find0(objects, n);
00395     }
00396   }
00397 
00398   /*--------------------------.
00399   | Free standing functions.  |
00400   `--------------------------*/
00401 
00403   void
00404   echo(const char* format, ...)
00405   {
00406     if (format)
00407     {
00408       // This method is deprecated, there's no good reason to
00409       // make it perfect.
00410       char buf[BUFSIZ];
00411       va_list arg;
00412       va_start(arg, format);
00413       // Don't print if we overflow the buffer.  It would be nice to
00414       // rely on the behavior of the GNU LibC which accepts 0 as
00415       // destination buffer to query the space needed.  But it is not
00416       // portable (e.g., segv on OS X).  So rather, try to vsnprintf,
00417       // and upon failure, revert the buffer in its previous state.
00418       int r = vsnprintf(buf, sizeof buf, format, arg);
00419       va_end(arg);
00420       // vsnprintf returns the number of characters to write.  Check
00421       // that it fits.  Don't forget the ending '\0' that it does not
00422       // count, but wants to add.
00423       if (r < 0 || static_cast<int>(sizeof buf) <= r)
00424         // Don't produce partial input.
00425         buf[sizeof buf - 1] = 0;
00426       GD_INFO_TRACE(buf);
00427     }
00428   }
00429 
00430   UTimerCallback::UTimerCallback(const std::string& objname,
00431                                  ufloat period,
00432                                  impl::UContextImpl* ctx)
00433     : period(period)
00434     , objname(objname)
00435     , ctx_(ctx)
00436   {
00437     lastTimeCalled = -9999999;
00438   }
00439 
00440   void
00441   UTimerCallback::registerCallback()
00442   {
00443      handle_ = ctx_->setTimer(this);
00444   }
00445 
00446   UTimerCallback::~UTimerCallback()
00447   {
00448   }
00449 
00450   std::string getFilteredHostname()
00451   {
00452     std::string res = libport::gethostname();
00453     if (!isalpha(res[0]))
00454       res = "_" + res;
00455     foreach (char& c, res)
00456       if (!isalnum(c) && c != '_')
00457         c = '_';
00458     return res;
00459   }
00460 }