|
Urbi SDK Remote for C++
2.7.5
|
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 }