|
Urbi SDK Remote for C++
2.7.3
|
00001 /* 00002 * Copyright (C) 2005-2011, 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 00012 00013 #include <iostream> 00014 #include <sstream> 00015 #include <list> 00016 00017 #include <boost/date_time/gregorian/gregorian.hpp> 00018 #include <boost/date_time/posix_time/posix_time.hpp> 00019 00020 #include <libport/containers.hh> 00021 #include <libport/debug.hh> 00022 #include <libport/escape.hh> 00023 #include <libport/foreach.hh> 00024 #include <libport/io-stream.hh> 00025 #include <libport/lexical-cast.hh> 00026 #include <libport/unistd.h> 00027 00028 #include <libport/debug.hh> 00029 #include <libport/format.hh> 00030 00031 #include <liburbi/compatibility.hh> 00032 00033 #include <urbi/uexternal.hh> 00034 #include <urbi/umessage.hh> 00035 #include <urbi/uobject.hh> 00036 #include <urbi/ustarter.hh> 00037 #include <urbi/usyncclient.hh> 00038 #include <urbi/uvalue-serialize.hh> 00039 #include <urbi/uvar.hh> 00040 #include <urbi/ucontext-factory.hh> 00041 #include <libuobject/remote-ucontext-impl.hh> 00042 00043 GD_CATEGORY(Urbi.LibUObject); 00044 00045 #define REQUIRE(Cond, ...) \ 00046 do { \ 00047 if (!(Cond)) \ 00048 { \ 00049 msg.client.printf(__VA_ARGS__); \ 00050 GD_FERROR("Message content: %s", *msg.value); \ 00051 return URBI_CONTINUE; \ 00052 } \ 00053 } while (false) 00054 00055 class HookPoint: public urbi::UObject 00056 { 00057 public: 00058 HookPoint(const std::string&n, urbi::impl::UContextImpl* impl) 00059 : UObject(n, impl) 00060 { 00061 UBindFunction(HookPoint, init); 00062 } 00063 int init() 00064 { 00065 return 1; 00066 } 00067 }; 00068 namespace urbi 00069 { 00070 UObjectMode running_mode() 00071 { 00072 return MODE_REMOTE; 00073 } 00074 00075 namespace impl 00076 { 00077 namespace 00078 { 00081 static 00082 void 00083 eval_call(UTable& t, UList& args) 00084 { 00085 if (UTable::callbacks_type* cs = t.find0(args[1])) 00086 { 00087 args.setOffset(2); 00088 foreach (UGenericCallback* c, *cs) 00089 c->eval(args); 00090 args.setOffset(0); 00091 } 00092 } 00093 } 00094 00095 typedef boost::unordered_map<std::string, impl::UContextImpl*> 00096 contexts_type; 00097 static contexts_type contexts; 00098 00099 impl::UContextImpl* 00100 makeRemoteContext(const std::string& host, const std::string& port) 00101 { 00102 return new impl::RemoteUContextImpl( 00103 new USyncClient(host, lexical_cast<unsigned>(port))); 00104 } 00105 00106 impl::UContextImpl* 00107 getRemoteContext(const std::string& host, const std::string& port) 00108 { 00109 std::string key = host + ':' + port; 00110 contexts_type::iterator i = contexts.find(key); 00111 if (i != contexts.end()) 00112 return i->second; 00113 return contexts[key] = makeRemoteContext(host, port); 00114 } 00115 00116 class SerializedUrbiscriptStreamBuffer: public libport::StreamBuffer 00117 { 00118 public: 00119 SerializedUrbiscriptStreamBuffer(RemoteUContextImpl* backend) 00120 : backend_(backend) 00121 { 00122 } 00123 protected: 00124 virtual size_t read(char*, size_t) 00125 { 00126 return 0; 00127 } 00128 virtual void write(char* buffer, size_t size) 00129 { 00130 char code = UEM_EVAL; 00131 std::string msg(buffer, size); 00132 backend_->backend_->startPack(); 00133 *backend_->oarchive << code << msg; 00134 backend_->backend_->flush(); 00135 backend_->backend_->endPack(); 00136 } 00137 private: 00138 RemoteUContextImpl* backend_; 00139 }; 00140 /*---------------------. 00141 | RemoteUContextImpl. | 00142 `---------------------*/ 00143 00144 RemoteUContextImpl::RemoteUContextImpl(USyncClient* client) 00145 : backend_(client) 00146 , closed_(false) 00147 , dummyUObject(0) 00148 , enableRTP(true) 00149 , dispatchDepth(0) 00150 , outputStream(client) 00151 , dataSent(false) 00152 , serializationMode(false) 00153 , oarchive(0) 00154 , sharedRTP_(0) 00155 { 00156 rtpSend = 0; 00157 rtpSendGrouped = 0; 00158 hookPointName_ = libport::format("hookPoint_%s_%s", 00159 getFilteredHostname(), 00160 #ifdef __UCLIBC__ 00161 "default" 00162 #else 00163 getpid() 00164 #endif 00165 ); 00166 backend_->setCallback(callback(*this, &RemoteUContextImpl::dispatcher), 00167 externalModuleTag.c_str()); 00168 backend_->setClientErrorCallback(callback(*this, 00169 &RemoteUContextImpl::clientError)); 00170 00171 typedef long long int us_t; 00172 UMessage* m = backend_->syncGet( 00173 "[System.timeReference.year," 00174 "System.timeReference.month," 00175 "System.timeReference.day," 00176 "System.timeReference.us," 00177 "PackageInfo.components[\"Urbi SDK\"].major," 00178 "PackageInfo.components[\"Urbi SDK\"].minor," 00179 "PackageInfo.components[\"Urbi SDK\"].subMinor," 00180 "PackageInfo.components[\"Urbi SDK\"].patch," 00181 "]" 00182 ); 00183 int year = (*m->value->list)[0]; 00184 int month = (*m->value->list)[1]; 00185 int day = (*m->value->list)[2]; 00186 int us = (*m->value->list)[3]; 00187 version = libport::PackageInfo::Version((*m->value->list)[4], 00188 (*m->value->list)[5], 00189 (*m->value->list)[6], 00190 (*m->value->list)[7]); 00191 // Compatibility for wire protocol 2.3-2.4. 00192 URBI_SEND_COMMAND_C 00193 (*outputStream, 00194 "if (!Object.hasSlot(\"uvalueSerialize\"))\n" 00195 " function Object.uvalueSerialize() { this }"); 00196 // Compatibility for versions < 2.7 00197 URBI_SEND_COMMAND_C 00198 (*outputStream, 00199 "if (!UObject.hasSlot(\"syncGet\"))\n" 00200 " function UObject.syncGet(exp, tag)\n" 00201 " {\n" 00202 " try { Channel.new(tag) << eval(exp) }\n" 00203 " catch (var e) { lobby.send(\"!!! \" + e, tag) }\n" 00204 " }"); 00205 00206 boost::posix_time::ptime now 00207 (boost::posix_time::microsec_clock::local_time()); 00208 boost::posix_time::ptime ref(boost::gregorian::date(year, month, day), 00209 boost::posix_time::microseconds(us)); 00210 libport::utime_reference_set 00211 (libport::utime() - (now - ref).total_microseconds()); 00212 GD_FINFO_DEBUG("Remote kernel reference timestamp: %s.", 00213 to_simple_string(ref)); 00214 GD_FINFO_DEBUG("Remote kernel version: %s", version); 00215 // Connect hookPoint 00216 setCurrentContext(this); 00217 new HookPoint(hookPointName_, const_cast<RemoteUContextImpl*>(this)); 00218 URBI_SEND_COMMAND_C(*outputStream, "var hookPoint = " 00219 + hookPointName_+"|"); 00220 } 00221 00222 RemoteUContextImpl::~RemoteUContextImpl() 00223 {} 00224 00225 std::string RemoteUContextImpl::hookPointName() 00226 { 00227 return hookPointName_; 00228 } 00229 00230 USyncClient* 00231 RemoteUContextImpl::getClient() 00232 { 00233 return backend_; 00234 } 00235 00236 UObject* 00237 RemoteUContextImpl::getDummyUObject() 00238 { 00239 if (!dummyUObject) 00240 dummyUObject = new UObject(0, this); 00241 return dummyUObject; 00242 } 00243 00244 void RemoteUContextImpl::uobject_unarmorAndSend(const char* a) 00245 { 00246 if (!serializationMode) 00247 unarmorAndSend(a, backend_); 00248 else 00249 { 00250 backend_->startPack(); 00251 size_t len = strlen(a); 00252 if (2 <= len && a[0] == '(') 00253 *outputStream << std::string(a+1, len-2); 00254 else 00255 *outputStream << std::string(a, len); 00256 markDataSent(); 00257 backend_->endPack(); 00258 } 00259 } 00260 00261 void RemoteUContextImpl::send(const char* a) 00262 { 00263 if (closed_) 00264 GD_FWARN("Write on closed remote context: \"%s\"", libport::escape(a)); 00265 else 00266 { 00267 backend_->startPack(); 00268 *outputStream << a; 00269 outputStream->flush(); 00270 backend_->endPack(); 00271 } 00272 } 00273 00274 void RemoteUContextImpl::send(const void* buf, size_t size) 00275 { 00276 if (closed_) 00277 GD_FWARN("Write on closed remote context: \"%s\"", 00278 libport::escape(std::string((const char*) buf, size))); 00279 else 00280 { 00281 backend_->startPack(); 00282 outputStream->rdbuf()->sputn(static_cast<const char*> (buf), size); 00283 outputStream->flush(); 00284 backend_->endPack(); 00285 } 00286 } 00287 00288 UObjectMode RemoteUContextImpl::getRunningMode() const 00289 { 00290 return MODE_REMOTE; 00291 } 00292 00293 UTable& 00294 RemoteUContextImpl::tableByName(const std::string& n) 00295 { 00296 #define CHECK(v) if (n == #v) return v##map##_ 00297 CHECK(access); 00298 CHECK(event); 00299 CHECK(eventend); 00300 CHECK(function); 00301 CHECK(monitor); 00302 #undef CHECK 00303 if (n == "var" || n =="var_onrequest") 00304 return monitormap_; 00305 if (n == "varaccess") 00306 return accessmap_; 00307 throw std::runtime_error("unexpected table name: " + n); 00308 } 00309 00310 std::pair<int, int> 00311 RemoteUContextImpl::kernelVersion() const 00312 { 00313 backend_->waitForKernelVersion(true); 00314 return std::make_pair(backend_->kernelMajor(), 00315 backend_->kernelMinor()); 00316 } 00317 00318 void 00319 RemoteUContextImpl::instanciated(UObject*) 00320 { 00321 // Protect our initialization code against rescoping by ','. 00322 send(";"); 00323 } 00324 00325 00326 void 00327 RemoteUContextImpl::lock() 00328 { 00329 } 00330 00331 void 00332 RemoteUContextImpl::unlock() 00333 { 00334 } 00335 00336 boost::asio::io_service& 00337 RemoteUContextImpl::getIoService() 00338 { 00339 return backend_->get_io_service(); 00340 } 00341 00342 /*--------------------. 00343 | RemoteUObjectImpl. | 00344 `--------------------*/ 00345 00346 RemoteUObjectImpl::~RemoteUObjectImpl() 00347 {} 00348 00350 void RemoteUObjectImpl::initialize(UObject* owner) 00351 { 00352 static int uid = 0; 00353 this->owner_ = owner; 00354 RemoteUContextImpl* ctx = dynamic_cast<RemoteUContextImpl*>(owner_->ctx_); 00355 //We were called by UObject base constructor. 00356 period = -1; 00357 if (owner->__name == "_dummy") 00358 return; 00359 bool fromcxx = owner_->__name.empty(); 00360 if (fromcxx) 00361 owner_->__name = "uob_" + getFilteredHostname() + string_cast(++uid); 00362 LockableOstream* client = ctx->outputStream; 00363 URBI_SEND_PIPED_COMMAND_C(*client, 00364 "class " << owner_->__name << "{}"); 00365 URBI_SEND_PIPED_COMMAND_C(*client, 00366 "external object " << owner_->__name); 00367 // Bind update, we need it since we use a dummy message locally generated 00368 // to trigger the periodic call. 00369 createUCallback(*owner, 0, "function", owner, &UObject::update, 00370 owner->__name + ".update"); 00371 00372 // At this point the child class constructor is called, and will 00373 // also send piped commands. 00374 // Then the starter will call instanciated() which will send a semicolon. 00375 // ...unless instanciation was made from c++. 00376 if (fromcxx) 00377 { // Delay calls to register functions until UObject constructor finishes, 00378 // othewrise typeid is wrong. 00379 ctx->addCleanup(boost::bind(&RemoteUContextImpl::instanciated, 00380 ctx, owner)); 00381 ctx->addCleanup(boost::bind(&UContextImpl::registerObject, ctx, owner)); 00382 } 00383 else 00384 owner_->ctx_->registerObject(owner); 00385 } 00386 00388 void 00389 RemoteUObjectImpl::clean() 00390 { 00391 RemoteUContextImpl& ctx = dynamic_cast<RemoteUContextImpl&> 00392 (*(owner_->ctx_)); 00393 00394 if (updateHandler) 00395 { 00396 updateHandler->cancel(); 00397 updateHandler.reset(); 00398 } 00399 00400 ctx.monitormap().clean(owner_->__name); 00401 ctx.accessmap().clean(owner_->__name); 00402 ctx.functionmap().clean(owner_->__name); 00403 ctx.eventmap().clean(owner_->__name); 00404 ctx.eventendmap().clean(owner_->__name); 00405 00406 if (owner_->objecthub) 00407 owner_->objecthub->members.remove(owner_); 00408 } 00409 00410 void 00411 RemoteUObjectImpl::setUpdate(ufloat t) 00412 { 00413 if (updateHandler) 00414 { 00415 updateHandler->cancel(); 00416 updateHandler.reset(); 00417 } 00418 period = t; 00419 onUpdate(); 00420 } 00421 00422 void 00423 RemoteUObjectImpl::onUpdate() 00424 { 00425 if (0 < period) 00426 { 00427 RemoteUContextImpl& ctx = 00428 dynamic_cast<RemoteUContextImpl&>(*(owner_->ctx_)); 00429 UMessage m(*ctx.backend_); 00430 m.type = MESSAGE_DATA; 00431 m.tag = externalModuleTag; 00432 m.value = new UValue(UList()); 00433 m.value->list->push_back(UEM_EVALFUNCTION); 00434 m.value->list->push_back(owner_->__name + ".update__0"); 00435 m.value->list->push_back(""); 00436 // This is potentialy not the worker thread, cannot call dispatcher() 00437 // synchronously. 00438 ctx.getClient()->notifyCallbacks(m); 00439 updateHandler = 00440 libport::asyncCall(boost::bind(&RemoteUObjectImpl::onUpdate, this), 00441 useconds_t(period * 1000)); 00442 } 00443 } 00444 00445 void RemoteUContextImpl::yield() const 00446 { 00447 yield_until(libport::utime()); 00448 } 00449 00450 void RemoteUContextImpl::yield_until(libport::utime_t deadline) const 00451 { 00452 // Ensure processEvents is called at least once. 00453 while (true) 00454 { 00455 bool processed = 00456 dynamic_cast<USyncClient*>(backend_)->processEvents(0); 00457 if (deadline < libport::utime()) 00458 return; 00459 if (!processed) 00460 usleep(0); 00461 } 00462 } 00463 00464 void RemoteUContextImpl::yield_until_things_changed() const 00465 { 00466 while (true) 00467 { 00468 if (dynamic_cast<USyncClient*>(backend_)->processEvents(0)) 00469 return; 00470 usleep(0); 00471 } 00472 } 00473 00474 void RemoteUContextImpl::side_effect_free_set(bool) 00475 {} 00476 00477 bool RemoteUContextImpl::side_effect_free_get() const 00478 { 00479 return false; 00480 } 00481 00482 static void 00483 call_result(RemoteUContextImpl* ctx, std::string var, 00484 const UValue& retval, const std::exception* e) 00485 { 00486 GD_FINFO_DUMP("... dispatch %s done", var); 00487 // var is empty for internally generated messages (such as update()) 00488 if (var.empty()) 00489 return; 00490 // This method can be called by a thread from the Thread Pool because 00491 // it is used as a callback function. Thus we have to declare the 00492 // category for the debugger used by the current thread. 00493 if (e) 00494 { 00495 URBI_SEND_COMMA_COMMAND_C 00496 (*ctx->outputStream, 00497 "Global.UObject.funCall(\"" << var << "\", " 00498 << "Exception.new(\"" 00499 << "Exception while calling remote bound method: " 00500 << libport::escape(e->what()) 00501 << "\"))"); 00502 } 00503 else 00504 { 00505 if (ctx->serializationMode) 00506 { 00507 char type = UEM_REPLY; 00508 ctx->outputStream->flush(); 00509 *ctx->oarchive << type << var << retval; 00510 ctx->backend_->flush(); 00511 } 00512 else 00513 switch (retval.type) 00514 { 00515 case DATA_BINARY: 00516 // Send it 00517 // URBI_SEND_COMMAND does not now how to send binary since it 00518 // depends on the kernel version. 00519 ctx->backend_->startPack(); 00520 *ctx->backend_ << "Global.UObject.funCall(\"" << var << "\", "; 00521 ctx->backend_->send(retval); 00522 *ctx->backend_ << "),\n"; 00523 ctx->backend_->endPack(); 00524 ctx->backend_->flush(); 00525 break; 00526 00527 case DATA_VOID: 00528 URBI_SEND_COMMAND_C 00529 (*ctx->outputStream, 00530 "Global.UObject.funCall(\"" << var << "\")"); 00531 break; 00532 00533 default: 00534 URBI_SEND_COMMA_COMMAND_C 00535 (*ctx->outputStream, 00536 "Global.UObject.funCall(\"" << var << "\", " << retval << ")"); 00537 break; 00538 } 00539 } 00540 } 00541 00542 UCallbackAction 00543 RemoteUContextImpl::dispatcher(const UMessage& msg) 00544 { 00545 if (closed_) 00546 return URBI_CONTINUE; 00547 00548 //check message type 00549 REQUIRE(msg.type == MESSAGE_DATA, 00550 "Component Error: unknown message content, type %d\n", 00551 msg.type); 00552 REQUIRE(msg.value->type == DATA_LIST, 00553 "Component Error: unknown message content, value type %d\n", 00554 msg.value->type); 00555 00556 UList& array = *msg.value->list; 00557 GD_FINFO_DUMP("Dispatching %s, first %s", array, array[0]); 00558 REQUIRE(array[0].type == DATA_DOUBLE, 00559 "Component Error: invalid server message type %d\n", 00560 array[0].type); 00561 00562 FINALLY(((unsigned int& , dispatchDepth)), dispatchDepth--); 00563 dispatchDepth++; 00564 setCurrentContext(this); 00565 switch ((USystemExternalMessage)(int)array[0]) 00566 { 00567 case UEM_ASSIGNVALUE: 00568 REQUIRE(array.size() == 4, 00569 "Component Error: invalid number " 00570 "of arguments in the server message: %lu (expected 4)\n", 00571 static_cast<unsigned long>(array.size())); 00572 assignMessage(array[1], array[2], array[3]); 00573 break; 00574 00575 case UEM_EVALFUNCTION: 00576 REQUIRE(3 <= array.size(), 00577 "Component Error: invalid number " 00578 "of arguments in the server message: %lu\n", 00579 static_cast<unsigned long>(array.size())); 00580 REQUIRE(array[2].type == DATA_STRING, 00581 "Component Error, argument 2 to function call is not a" 00582 "stirng\n"); 00583 evalFunctionMessage(array[1], array[2], array); 00584 break; 00585 00586 case UEM_EMITEVENT: 00587 eval_call(eventmap(), array); 00588 break; 00589 00590 case UEM_ENDEVENT: 00591 eval_call(eventendmap(), array); 00592 break; 00593 00594 case UEM_NEW: 00595 { 00596 impl::UContextImpl::CleanupStack s_(*this); 00597 objects_type::iterator i = objects.find(std::string(array[2])); 00598 REQUIRE(i != objects.end(), 00599 "No such objects %s\n", std::string(array[2]).c_str()); 00600 baseURBIStarter* bsa = i->second->cloner; 00601 REQUIRE(bsa, "Object %s has no cloner", std::string(array[2]).c_str()); 00602 GD_FINFO_DEBUG("instantiating from %s (%s), name: %s", bsa, array[2], 00603 array[1]); 00604 bsa->instanciate(this, (std::string) array[1]); 00605 } 00606 break; 00607 00608 case UEM_DELETE: 00609 { 00610 impl::UContextImpl::CleanupStack s_(*this); 00611 objects_type::iterator i = objects.find(std::string(array[1])); 00612 if (i == objects.end()) 00613 GD_FWARN("delete: no such object: %s", array[1]); 00614 else 00615 { 00616 delete i->second; 00617 objects.erase(i); 00618 if (objects.empty()) 00619 { 00620 // All the instances have been deleted, we're done with this 00621 // remote. 00622 GD_INFO_TRACE("Last instance deleted, quit"); 00623 exit(0); 00624 } 00625 } 00626 } 00627 break; 00628 00629 case UEM_INIT: 00630 init(); 00631 // switch to binary mode 00632 if (!getenv("URBI_TEXT_MODE") 00633 && version >= libport::PackageInfo::Version(2, 6, 0, 13)) 00634 { 00635 GD_INFO_TRACE("Switching to binary mode"); 00636 setSerializationMode(true); 00637 } 00638 break; 00639 00640 case UEM_TIMER: 00641 { 00642 std::string cbname = array[1]; 00643 TimerMap::iterator i = timerMap.find(cbname); 00644 if (i != timerMap.end()) 00645 i->second.second->call(); 00646 } 00647 break; 00648 00649 case UEM_NORTP: 00650 GD_WARN("Disabling RTP as requested by engine"); 00651 enableRTP = false; 00652 break; 00653 00654 case UEM_SETRTP: 00655 REQUIRE(array.size() == 3, 00656 "Component Error: invalid number " 00657 "of arguments in the server message: %lu (expected 3)\n", 00658 static_cast<unsigned long>(array.size())); 00659 setRTPMessage(array[1], array[2]); 00660 break; 00661 00662 case UEM_SETLOCAL: 00663 { 00664 REQUIRE(array.size() == 3, 00665 "Component Error: invalid number " 00666 "of arguments in the server message: %lu (expected 3)\n", 00667 static_cast<unsigned long>(array.size())); 00668 std::string name = array[1]; 00669 bool state = array[2]; 00670 GD_FINFO_LOG("Set local mode to %s on %s", state, name); 00671 { 00672 libport::BlockLock bl(tableLock); 00673 if (std::list<UVar*> *us = varmap().find0(name)) 00674 { 00675 foreach(UVar* v, *us) 00676 v->set_local(state); 00677 } 00678 } 00679 } 00680 break; 00681 00682 default: 00683 REQUIRE(false, 00684 "Component Error: unknown server message type number %d\n", 00685 (int)array[0]); 00686 } 00687 00688 // Send a terminating ';' since code send by the UObject API uses '|'. 00689 // But only in outermost dispatch call 00690 if (dispatchDepth == 1 && dataSent) 00691 { 00692 URBI_SEND_COMMAND_C(*outputStream, ""); 00693 dataSent = false; 00694 } 00695 return URBI_CONTINUE; 00696 } 00697 00698 void 00699 RemoteUContextImpl::assignMessage(const std::string& name, 00700 const UValue& v, time_t ts, 00701 bool bypass, 00702 UValue* val, time_t* timestamp) 00703 { 00704 libport::BlockLock bl(tableLock); 00705 std::list<UVar*> *us = 0; 00706 bool cachedVal = val; 00707 // Fetch storage UValue if it was not given to us 00708 if (!val) 00709 { 00710 us = varmap().find0(name); // Do not make this call if cachedVal 00711 if (us && !us->empty()) 00712 { 00713 // Get first UVarImpl to get pointers to val and timestamp 00714 RemoteUVarImpl* vimpl = 00715 static_cast<RemoteUVarImpl*>(us->front()->impl_); 00716 val = vimpl->value_; 00717 timestamp = vimpl->timestamp_; 00718 } 00719 } 00720 if (val) 00721 { 00722 val->set(v, bypass); 00723 *timestamp = ts; 00724 } 00725 // Process notifyChange 00726 if (UTable::callbacks_type* cs = monitormap().find0(name)) 00727 { 00728 foreach (UGenericCallback *c, *cs) 00729 { 00730 // test of return value here 00731 UList u; 00732 u.array.push_back(new UValue()); 00733 u[0].storage = c->target; 00734 c->eval(u); 00735 } 00736 } 00737 /* Reset val to empty uvalue in bypass mode 00738 * if val was not given to us as argument, maybe it was destroyed since 00739 * we calculated it. So check that at least one UVar is still present. 00740 */ 00741 if (bypass && (cachedVal || (us && !us->empty()))) 00742 { // Reset to void 00743 val->set(UValue()); 00744 } 00745 } 00746 00747 void 00748 RemoteUContextImpl::evalFunctionMessage(const std::string& name, 00749 const std::string& var, 00750 UList& args) 00751 { 00752 GD_FINFO_DUMP("dispatch call %s = %s...", var, name); 00753 UGenericCallback* cb = 0; 00754 { 00755 libport::BlockLock bl(tableLock); 00756 UTable::callbacks_type funs = functionmap()[name]; 00757 UTable::callbacks_type::iterator i = funs.begin(); 00758 if (i == funs.end()) 00759 throw std::runtime_error("no callback found"); 00760 cb = *i; 00761 } 00762 args.setOffset(3); 00763 cb->eval(args, 00764 boost::bind(&call_result, this, var, _1, _2)); 00765 GD_INFO_DUMP("dispatch call over, async call_result"); 00766 } 00767 00768 void 00769 RemoteUContextImpl::setRTPMessage(const std::string& varname, 00770 int state) 00771 { 00772 libport::BlockLock bl(tableLock); 00773 if (std::list<UVar*> *us = varmap().find0(varname)) 00774 { 00775 foreach (UVar* u, *us) 00776 { 00777 u->useRTP(state?UVar::RTP_YES: UVar::RTP_NO); 00778 } 00779 } 00780 } 00781 00782 void 00783 RemoteUContextImpl::newUObjectClass(baseURBIStarter* s) 00784 { 00785 s->instanciate(this); 00786 } 00787 void 00788 RemoteUContextImpl::newUObjectHubClass(baseURBIStarterHub* s) 00789 { 00790 s->instanciate(this); 00791 } 00792 00793 /*---------------------. 00794 | UObjects accessors. | 00795 `---------------------*/ 00796 00797 TimerHandle RemoteUContextImpl::setTimer(UTimerCallback* cb) 00798 { 00799 cb->call(); 00800 libport::AsyncCallHandler h = 00801 libport::asyncCall(boost::bind(&RemoteUContextImpl::onTimer, this, cb), 00802 useconds_t(cb->period * 1000)); 00803 libport::BlockLock bl(mapLock); 00804 std::string cbname = "timer" + string_cast(cb); 00805 timerMap[cbname] = std::make_pair(h, cb); 00806 return TimerHandle(new std::string(cbname)); 00807 } 00808 00809 void 00810 RemoteUContextImpl::onTimer(UTimerCallback* cb) 00811 { 00812 std::string cbname = "timer" + string_cast(cb); 00813 { 00814 libport::BlockLock bl(mapLock); 00815 if (!libport::mhas(timerMap, cbname)) 00816 return; 00817 } 00818 backend_->notifyCallbacks 00819 (UMessage(*backend_, 0, externalModuleTag, 00820 libport::format("[%s,\"%s\"]", UEM_TIMER, cbname))); 00821 00822 libport::BlockLock bl(mapLock); 00823 libport::AsyncCallHandler h = 00824 libport::asyncCall(boost::bind(&RemoteUContextImpl::onTimer, this, cb), 00825 useconds_t(cb->period * 1000)); 00826 timerMap[cbname] = std::make_pair(h, cb); 00827 } 00828 00829 bool 00830 RemoteUObjectImpl::removeTimer(TimerHandle h) 00831 { 00832 if (!h) 00833 return false; 00834 RemoteUContextImpl* ctx = dynamic_cast<RemoteUContextImpl*>(owner_->ctx_); 00835 libport::BlockLock bl(ctx->mapLock); 00836 // Should not happen, but you never know... 00837 if (!libport::mhas(ctx->timerMap, *h)) 00838 return false; 00839 ctx->timerMap[*h].first->cancel(); 00840 ctx->timerMap.erase(*h); 00841 h.reset(); 00842 return true; 00843 } 00844 00845 void 00846 RemoteUContextImpl::call(const std::string& object, 00847 const std::string& method, 00848 UAutoValue v1, 00849 UAutoValue v2, 00850 UAutoValue v3, 00851 UAutoValue v4, 00852 UAutoValue v5, 00853 UAutoValue v6) 00854 { 00855 std::stringstream s; 00856 s << object << "." << method <<"("; 00857 #define CHECK(v) if (v.type != DATA_VOID) s << v << "," 00858 CHECK(v1); CHECK(v2); CHECK(v3); CHECK(v4); 00859 CHECK(v5); CHECK(v6); 00860 #undef CHECK 00861 std::string r = s.str(); 00862 if (v1.type != DATA_VOID) 00863 r = r.substr(0, r.length() - 1); 00864 r += ')'; 00865 URBI_SEND_COMMA_COMMAND_C(*outputStream, r); 00866 markDataSent(); 00867 } 00868 00869 void 00870 RemoteUContextImpl::declare_event(const UEvent* owner) 00871 { 00872 // Event may or may not already exist. 00873 std::string r = "try{var " + owner->get_name() + " = Event.new()}" 00874 " catch(var e) {}"; 00875 URBI_SEND_PIPED_COMMAND_C(*outputStream, r); 00876 markDataSent(); 00877 } 00878 00879 void 00880 RemoteUContextImpl::emit(const std::string& object, 00881 UAutoValue& v1, 00882 UAutoValue& v2, 00883 UAutoValue& v3, 00884 UAutoValue& v4, 00885 UAutoValue& v5, 00886 UAutoValue& v6, 00887 UAutoValue& v7) 00888 { 00889 if (serializationMode) 00890 { 00891 UAutoValue* vals[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7}; 00892 int i = 0; 00893 while (i<7 && vals[i]->type != DATA_VOID) 00894 ++i; 00895 outputStream->flush(); 00896 char code = UEM_EMITEVENT; 00897 *oarchive << code << object << i; 00898 for (int t=0; t<i; ++t) 00899 *oarchive << *(UValue*)vals[t]; 00900 backend_->flush(); 00901 return; 00902 } 00903 std::stringstream s; 00904 s << object << "!("; 00905 #define CHECK(v) if (v.type != DATA_VOID) s << v << "," 00906 CHECK(v1); CHECK(v2); CHECK(v3); CHECK(v4); 00907 CHECK(v5); CHECK(v6); CHECK(v7); 00908 #undef CHECK 00909 std::string r = s.str(); 00910 if (v1.type != DATA_VOID) 00911 r = r.substr(0, r.length() - 1); 00912 r += ')'; 00913 URBI_SEND_COMMAND_C(*outputStream, r); 00914 markDataSent(); 00915 } 00916 00917 UValue 00918 RemoteUContextImpl::localCall(const std::string& object, 00919 const std::string& method, 00920 UAutoValue v1, 00921 UAutoValue v2, 00922 UAutoValue v3, 00923 UAutoValue v4, 00924 UAutoValue v5, 00925 UAutoValue v6, 00926 UAutoValue v7, 00927 UAutoValue v8) 00928 { 00929 UAutoValue* vals[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8}; 00930 int nargs = 0; 00931 while (nargs<8 && vals[nargs]->type != DATA_VOID) 00932 ++nargs; 00933 std::string name = object + "." + method +"__" + string_cast(nargs); 00934 UList l; 00935 { 00936 // We do not copy the UValues, so do not let the UList destroy them. 00937 FINALLY(((UList&, l)), l.array.clear()); 00938 for (int i=0; i<nargs; ++i) 00939 l.array.push_back(vals[i]); 00940 UGenericCallback* cb; 00941 { 00942 libport::BlockLock bl(tableLock); 00943 UTable::callbacks_type tmpfun = functionmap()[name]; 00944 UTable::callbacks_type::iterator tmpfunit = tmpfun.begin(); 00945 if (tmpfunit == tmpfun.end()) 00946 throw std::runtime_error("no callback found for " + object +"::" 00947 + method + " with " + string_cast(nargs) 00948 +" arguments"); 00949 cb = *tmpfunit; 00950 } 00951 return cb->__evalcall(l); 00952 } 00953 } 00954 00955 UVarImpl* 00956 RemoteUContextImpl::getVarImpl() 00957 { 00958 return new RemoteUVarImpl(); 00959 } 00960 00961 UObjectImpl* 00962 RemoteUContextImpl::getObjectImpl() 00963 { 00964 return new RemoteUObjectImpl(); 00965 } 00966 00967 UGenericCallbackImpl* 00968 RemoteUContextImpl::getGenericCallbackImpl() 00969 { 00970 return new RemoteUGenericCallbackImpl(); 00971 } 00972 00973 /*-------------. 00974 | UObjectHub. | 00975 `-------------*/ 00976 00977 void 00978 RemoteUContextImpl::setHubUpdate(UObjectHub*, ufloat) 00979 { 00980 // nothing happend in remote mode... 00981 } 00982 void 00983 RemoteUContextImpl::removeHub(UObjectHub*) 00984 { 00985 } 00986 void 00987 RemoteUContextImpl::registerHub(UObjectHub*) 00988 { 00989 } 00990 00991 UCallbackAction 00992 RemoteUContextImpl::clientError(const UMessage&) 00993 { 00994 GD_INFO_TRACE("clientError on remote context"); 00995 if (closed_) 00996 { 00997 GD_WARN("ClientError already processed"); 00998 return URBI_CONTINUE; 00999 } 01000 impl::UContextImpl::CleanupStack s_(*this); 01001 closed_ = true; 01002 /* Destroy everything 01003 * We must remove each object from the hash right after deleting it 01004 * to prevent getUObject requests on deleted items from dtor of other 01005 * uobjects. 01006 * Clearing first then deleting might make some UObject fail, since 01007 * getUObject would return 0 for perfectly valid and accessible UObjects. 01008 */ 01009 while (!objects.empty()) 01010 { 01011 objects_type::iterator i = objects.begin(); 01012 GD_FINFO_TRACE("Destroying object %s", i->second); 01013 delete i->second; 01014 objects.erase(i); 01015 } 01016 01017 while (!hubs.empty()) 01018 { 01019 hubs_type::iterator i = hubs.begin(); 01020 GD_FINFO_TRACE("Destroying hub %s", i->second); 01021 delete i->second; 01022 hubs.erase(i); 01023 } 01024 return URBI_CONTINUE; 01025 } 01026 01027 void RemoteUContextImpl::setSerializationMode(bool mode) 01028 { 01029 if (mode == serializationMode) 01030 return; 01031 if (!mode) 01032 throw std::runtime_error("Serialization mode can not be undone"); 01033 serializationMode = mode; 01034 // Notify the kernel, in the current mode. 01035 // Do not use call, we must be foreground. 01036 // Do not send any other message until we get a reply. 01037 backend_->lockQueue(); 01038 const char* tag = "remotecontext_setmode"; 01039 send(libport::format("binaryMode(%s, \"%s\");\n", mode, tag)); 01040 delete backend_->waitForTag(tag, 0); 01041 // Change the urbiscript outputstream to one that encapsulates 01042 // in UValue and serializes. 01043 if (mode) 01044 { 01045 if (!oarchive) 01046 oarchive = new libport::serialize::BinaryOSerializer(*backend_); 01047 outputStream = 01048 new LockableOstream(new SerializedUrbiscriptStreamBuffer(this)); 01049 } 01050 else 01051 { 01052 // Reset outputstream to the USyncClient. 01053 delete outputStream->rdbuf(); 01054 delete outputStream; 01055 outputStream = backend_; 01056 } 01057 } 01058 01059 UMessage* 01060 RemoteUContextImpl::syncGet(const std::string& exp, 01061 libport::utime_t timeout) 01062 { 01063 static int counter = 0; 01064 counter++; 01065 std::string tag = "remotecontext_" + string_cast(counter); 01066 backend_->lockQueue(); 01067 call("UObject", "syncGet", exp, tag); 01068 return backend_->waitForTag(tag, timeout); 01069 } 01070 01071 void 01072 RemoteUContextImpl::markDataSent() 01073 { 01074 if (backend_->isCallbackThread() && dispatchDepth) 01075 { 01076 if (!serializationMode) // No it cannot go in the if above. 01077 dataSent = true; 01078 } 01079 else // we were not called by dispatch: send the terminating ';' ourselve. 01080 URBI_SEND_COMMAND_C((*outputStream), ""); 01081 } 01082 } // namespace urbi::impl 01083 01084 /* 01085 FIXME: find out where it is used 01086 std::string 01087 baseURBIStarter::getFullName(const std::string& name) const 01088 { 01089 if (local) 01090 return name + "_" + getClientConnectionID(outputStream); 01091 else 01092 return name; 01093 }*/ 01094 } // namespace urbi