void StrandPrivate::enqueue(boost::shared_ptr<Callback> cbStruct) { qiLogDebug() << "Enqueueing job id " << cbStruct->id; bool shouldschedule = false; { boost::mutex::scoped_lock lock(_mutex); // the callback may have been canceled if (cbStruct->state == State_None) { _queue.push_back(cbStruct); cbStruct->state = State_Scheduled; } else qiLogDebug() << "Job is not schedulable, state " << (int)cbStruct->state; // if process was not scheduled yet, do it, there is work to do if (!_processing) { _processing = true; shouldschedule = true; } } if (shouldschedule) { qiLogDebug() << "StrandPrivate::process was not scheduled, doing it"; _eventLoop.async(boost::bind(&StrandPrivate::process, shared_from_this()), qi::Duration(0)); } }
// Find out our async parent and add to it static bool insertAsyncParentTrace(CallList& l, CallData* d) { if (l.empty()) { qiLogDebug() << "empty"; return false; } qiLogDebug() << l.front()->tStart; // Get first entry that started after our post time. CallList::iterator it = std::upper_bound(l.begin(), l.end(), CallTime(d->tPost)); if (it == l.begin()) { // damm, first element is older than us qiLogInfo() << "No async parent can be found"; return false; } --it; qiLogDebug() << "Child check"; // try if a sync child is better placed than it bool wasInserted = insertAsyncParentTrace((*it)->children, d); if (wasInserted) return true; // was not inserted in children, insert here (*it)->asyncChildren.push_back(d); d->asyncParent = *it; return true; }
void RemoteObject::close(const std::string& reason, bool fromSignal) { qiLogDebug() << "Closing remote object"; TransportSocketPtr socket; { boost::mutex::scoped_lock lock(_socketMutex); socket = _socket; _socket.reset(); } if (socket) { // Do not hold any lock when invoking signals. qiLogDebug() << "Removing connection from socket " << (void*)socket.get(); socket->messagePendingDisconnect(_service, TransportSocket::ALL_OBJECTS, _linkMessageDispatcher); if (!fromSignal) socket->disconnected.disconnect(_linkDisconnected); } std::map<int, qi::Promise<AnyReference> > promises; { boost::mutex::scoped_lock lock(_promisesMutex); promises = _promises; _promises.clear(); } // Nobody should be able to add anything to promises at this point. std::map<int, qi::Promise<AnyReference> >::iterator it; for (it = promises.begin(); it != promises.end(); ++it) { qiLogVerbose() << "Reporting error for request " << it->first << "(" << reason << ")"; it->second.setError(reason); } //@warning: remove connection are not removed // not very important ATM, because RemoteObject // cant be reconnected }
TEST(Proxy, Property) { boost::shared_ptr<Bar> bar(new Bar); qi::AnyObject gbar = qi::AnyReference::from(bar).toObject(); ASSERT_TRUE(!!gbar); // The session must die before bar. TestSessionPair p; p.server()->registerService("bar", gbar); // we need that to force two clients p.server()->registerService("bar2", gbar); qi::AnyObject client = p.client()->service("bar"); ASSERT_EQ(0, client.call<int>("sum")); qi::ProxyProperty<int> pp(client, "prop"); bar->set(1); ASSERT_EQ(1, pp.get()); pp.set(2); ASSERT_EQ(2, bar->get()); // althoug PropertyProxy::set is itself synchronous, notify on remote end // may be asynchronous, so subscribe below may come too soon and catch // the pp.set above qi::os::msleep(100); qiLogDebug() << "subscribe"; bar->subscribe(); qi::os::msleep(100); qiLogDebug() << "set 3"; pp.set(3); // this is an event, all notify are asychronous PERSIST_ASSERT(, bar->sum() == 3, 500); Bar bar2; qi::SignalLink l = pp.connect(boost::bind(&Bar::onProp, &bar2, _1)); bar->set(4); // this one is async (remote notify of local property set) PERSIST_ASSERT(, bar2.sum() == 4, 500); pp.disconnect(l); bar->set(5); // we expect an async op *not* to happen, no choice but wait. qi::os::msleep(200); ASSERT_EQ(4, bar2.sum()); // reconnect to see if disconnect did not break anything l = pp.connect(boost::bind(&Bar::onProp, &bar2, _1)); bar->set(4); PERSIST_ASSERT(, bar2.sum() == 8, 500); // proxy-proxy qi::AnyObject client2 = p.client()->service("bar2"); qi::ProxyProperty<int> pp2(client2, "prop"); Bar bar3; pp2.connect(boost::bind(&Bar::onProp, &bar3, _1)); qiLogDebug() << "set 2"; pp.set(2); PERSIST(, bar3.sum() == 2, 1000); ASSERT_EQ(2, bar3.sum()); PERSIST(, bar2.sum() == 10, 500); ASSERT_EQ(10, bar2.sum()); qiLogDebug() << "set 3"; pp2.set(3); PERSIST_ASSERT(, bar2.sum() == 13, 500); PERSIST_ASSERT(, bar3.sum() == 5, 500); }
TEST(QiSession, GettingServiceWhileDisconnecting) { qi::SessionPtr server = qi::makeSession(); server->listenStandalone("tcp://0.0.0.0:0"); qi::DynamicObjectBuilder builder; qi::AnyObject object(builder.object()); std::string serviceName = "sarace"; server->registerService(serviceName, object); qi::SessionPtr client = qi::makeSession(); for(int i = 0; i < 1000; ++i) { client->connect(server->endpoints()[0]); qi::Future<void> closing = client->close().async(); try { qi::AnyObject remoteObject = client->service(serviceName); bool remoteObjectWasFound = remoteObject; ASSERT_TRUE(remoteObjectWasFound); } catch(const qi::FutureException& e) { qiLogDebug() << "Got expected error: " << e.what(); } catch(const std::exception& e) { qiLogDebug() << "Got standard error: " << e.what(); } closing.wait(); } }
qi::Future<SignalLink> RemoteObject::metaConnect(unsigned int event, const SignalSubscriber& sub) { qi::Promise<SignalLink> prom(qi::FutureCallbackType_Sync); // Bind the subscriber locally. SignalLink uid = DynamicObject::metaConnect(event, sub); boost::recursive_mutex::scoped_lock _lock(_localToRemoteSignalLinkMutex); // maintain a map of localsignal -> remotesignal //(we only use one remotesignal, for many locals) LocalToRemoteSignalLinkMap::iterator it; RemoteSignalLinks& rsl = _localToRemoteSignalLink[event]; rsl.localSignalLink.push_back(uid); if (rsl.remoteSignalLink == qi::SignalBase::invalidSignalLink) { /* Try to handle struct versionning. * Hypothesis: Everything in this address space uses the same struct * version. It makes sense since typesystem does not handle conflicting * definitions for the same type name (due to global typeid->typeinterface factory). * * So we use the very first subscriber to try to detect a version mismatch * between what the callback expects, and what the signal advertises. * If so we inform the remote end to try to convert for us. */ Signature subSignature = sub.signature(); float score = 1; if (subSignature.isValid()) { const MetaSignal* ms = metaObject().signal(event); if (!ms) return makeFutureError<SignalLink>("Signal not found"); score = ms->parametersSignature().isConvertibleTo(subSignature); qiLogDebug() << "Conversion score " << score << " " << ms->parametersSignature().toString() << " -> " << subSignature.toString(); if (!score) { std::ostringstream ss; ss << "Subscriber not compatible to signal signature: cannot convert " << ms->parametersSignature().toString() << " to " << subSignature.toString(); return makeFutureError<SignalLink>(ss.str()); } } rsl.remoteSignalLink = uid; qiLogDebug() << "connect() to " << event << " gave " << uid << " (new remote connection)"; if (score >= 0.2) rsl.future = _self.async<SignalLink>("registerEvent", _service, event, uid); else // we might or might not be capable to convert, ask the remote end to try also rsl.future = _self.async<SignalLink>("registerEventWithSignature", _service, event, uid, subSignature.toString()); } else { qiLogDebug() << "connect() to " << event << " gave " << uid << " (reusing remote connection)"; } rsl.future.connect(boost::bind<void>(&onEventConnected, this, _1, prom, uid)); return prom.future(); }
qi::Buffer replyBuf(const qi::Buffer& buf) { qiLogDebug() << "enter"; if (msDelay) qi::os::msleep(msDelay); qi::Buffer res = qi::Buffer(buf); qiLogDebug() << "leave"; return res; }
/// The number of records. size_t recordCount() const { qiLogDebug("qi.signalspy") << "Getting record count " << (strand()->isInThisContext() ? "from strand" : "from outside"); return async([this] { qiLogDebug("qi.signalspy") << "Getting record count"; return _records.size(); }).value(); }
qi::FutureSync<void> ServiceDirectoryClient::onSocketDisconnected(std::string error) { qi::Future<void> fut; { qi::TransportSocketPtr socket; { // can't hold lock while disconnecting signals, so swap _sdSocket. boost::mutex::scoped_lock lock(_mutex); std::swap(socket, _sdSocket); } if (!socket) return qi::Future<void>(0); // We just manually triggered onSocketDisconnected, so unlink // from socket signal before disconnecting it. socket->disconnected.disconnect(_sdSocketDisconnectedSignalLink); // Manually trigger close on our remoteobject or it will be called // asynchronously from socket.disconnected signal, and we would need to // wait fo it. _remoteObject.close(); fut = socket->disconnect(); // Hold the socket shared ptr alive until the future returns. // otherwise, the destructor will block us until disconnect terminates // Nasty glitch: socket is reusing promises, so this future hook will stay // So pass shared pointer by pointer: that way a single delete statement // will end all copies. fut.connect(&sharedPtrHolder, new TransportSocketPtr(socket)); } qi::SignalLink add=0, remove=0; qi::AnyObject object; { boost::mutex::scoped_lock lock(_mutex); std::swap(add, _addSignalLink); std::swap(remove, _removeSignalLink); } try { if (add != 0) { _object.disconnect(add); } } catch (std::runtime_error &e) { qiLogDebug() << "Cannot disconnect SDC::serviceAdded: " << e.what(); } try { if (remove != 0) { _object.disconnect(remove); } } catch (std::runtime_error &e) { qiLogDebug() << "Cannot disconnect SDC::serviceRemoved: " << e.what(); } disconnected(error); return fut; }
/// Direct access to a record, by order of arrival. Record record(size_t index) const { qiLogDebug("qi.signalspy") << "Getting record #" << index << " " << (strand()->isInThisContext() ? "from strand" : "from outside"); return async([this, index] { qiLogDebug("qi.signalspy") << "Getting record #" << index; return _records[index]; }).value(); }
void TimelinePrivate::stop(bool join) { qiLogDebug() << "Stopping timeline " << _name; _executer->stopExecuter(join); { boost::unique_lock<boost::recursive_mutex> lock(_methodMonitor); killMotionOrders(); } qiLogDebug() << "Timeline " << _name << " stopped"; }
unsigned int ServiceDirectory::registerService(const ServiceInfo &svcinfo) { boost::shared_ptr<ServiceBoundObject> sbo = serviceBoundObject.lock(); if (!sbo) throw std::runtime_error("ServiceBoundObject has expired."); TransportSocketPtr socket = sbo->currentSocket(); boost::recursive_mutex::scoped_lock lock(mutex); std::map<std::string, unsigned int>::iterator it; it = nameToIdx.find(svcinfo.name()); if (it != nameToIdx.end()) { std::stringstream ss; ss << "Service \"" << svcinfo.name() << "\" (#" << it->second << ") is already registered. " << "Rejecting conflicting registration attempt."; qiLogWarning() << ss.str(); throw std::runtime_error(ss.str()); } unsigned int idx = ++servicesCount; nameToIdx[svcinfo.name()] = idx; // Do not add serviceDirectory on the map (socket() == null) if (idx != qi::Message::Service_ServiceDirectory) socketToIdx[socket].push_back(idx); pendingServices[idx] = svcinfo; pendingServices[idx].setServiceId(idx); idxToSocket[idx] = socket; std::stringstream ss; ss << "Registered Service \"" << svcinfo.name() << "\" (#" << idx << ")"; if (! svcinfo.name().empty() && svcinfo.name()[0] == '_') { // Hide services whose name starts with an underscore qiLogDebug() << ss.str(); } else { qiLogInfo() << ss.str(); } qi::UrlVector::const_iterator jt; for (jt = svcinfo.endpoints().begin(); jt != svcinfo.endpoints().end(); ++jt) { qiLogDebug() << "Service \"" << svcinfo.name() << "\" is now on " << jt->str(); } return idx; }
void *Buffer::read(size_t off, size_t length) const { if (!_p) { qiLogDebug("qi.buffer") << "read on empty buffer"; return 0; } if (off + length > _p->used) { qiLogDebug("qi.buffer") << "Attempt to read " << off+length <<" on buffer of size " << _p->used; return 0; } return (char*)_p->data() + off; }
bool ParameterModel::addChoice(ChoiceModelPtr choice) { qiLogDebug() << "addChoice function" << std::endl; Signature signature(_p->_metaProperty.signature()); //if false choice and parameter are the same type if(Signature(choice->value().signature()).isConvertibleTo(signature) < 1.0f ) { qiLogWarning() << "choice.type (i.e " << choice->value().signature().toString() << ") != parameter.type (i.e " << _p->_defaultValue.signature().toString() <<")" << std::endl; return false; } //If choice.value is not in [parameter.min, paramater.max] then the choice //is incorrect if(!_p->inInterval(choice->value(), _p->_min, _p->_max) ) { qiLogInfo() << "Choice : is not in interval" << std::endl; return false; } _p->_choices.push_front(choice); return true; }
RemoteObject::~RemoteObject() { qiLogDebug() << "~RemoteObject " << this; //close may already have been called. (by Session_Service.close) close("RemoteObject destroyed"); destroy(); }
void *dlopen(const char *filename, int flag) { g_LastError.reset(); std::string fullName = path::findLib(filename); if (fullName.empty()) { qiLogVerbose() << "Could not locate library " << filename; fullName = filename; // Do not return here, let sys call fails and set errno. if (fullName.empty()) { // do not allow dlopen(""), it will return a valid handler to the // current process g_LastError.reset(const_cast<char*>("trying to dlopen empty filename")); return NULL; } } void *handle = NULL; boost::filesystem::path fname(fullName, qi::unicodeFacet()); qiLogDebug() << "opening " << fname; #ifdef _WIN32 handle = LoadLibraryW(fname.wstring(qi::unicodeFacet()).c_str()); #else if (flag == -1) flag = RTLD_NOW; handle = ::dlopen(fname.string(qi::unicodeFacet()).c_str(), flag); #endif return handle; }
void TimelinePrivate::startFlowdiagramAsync(int index) { qiLogDebug() << "Start Flowdiagram, frames : " << index; boost::thread* t = new boost::thread(boost::bind(&TimelinePrivate::startFlowdiagram, this, index)); _flowdiagrams.push_back(t); }
void TransportSocketCache::close() { qiLogDebug() << "TransportSocketCache is closing"; ConnectionMap map; std::list<TransportSocketPtr> pending; { boost::mutex::scoped_lock lock(_socketMutex); _dying = true; std::swap(map, _connections); std::swap(pending, _allPendingConnections); } for (ConnectionMap::iterator mIt = map.begin(), mEnd = map.end(); mIt != mEnd; ++mIt) { for (std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.begin(), uEnd = mIt->second.end(); uIt != uEnd; ++uIt) { TransportSocketPtr endpoint = uIt->second->endpoint; // Disconnect any valid socket we were holding. if (endpoint) { endpoint->disconnect(); } else { uIt->second->state = State_Error; uIt->second->promise.setError("TransportSocketCache is closing."); } } } for (std::list<TransportSocketPtr>::iterator it = pending.begin(), end = pending.end(); it != end; ++it) (*it)->disconnect(); }
void PeriodicTaskPrivate::_reschedule(qi::int64_t delay) { qiLogDebug() << _name <<" rescheduling in " << delay; _task = getEventLoop()->async(boost::bind(&PeriodicTaskPrivate::_wrap, shared_from_this()), delay); if (!_state.setIfEquals(Task_Rescheduling, Task_Scheduled)) qiLogError() << "PeriodicTask forbidden state change while rescheduling " << *_state; }
void RemoteObject::onFutureCancelled(unsigned int originalMessageId) { qiLogDebug() << "Cancel request for message " << originalMessageId; TransportSocketPtr sock; { boost::mutex::scoped_lock lock(_socketMutex); sock = _socket; } Message cancelMessage; if (!sock) { qiLogWarning() << "Tried to cancel a call, but the socket to service " << _service << " is disconnected."; return; } if (!sock->sharedCapability<bool>("RemoteCancelableCalls", false)) { qiLogWarning() << "Remote end does not support cancelable calls."; return; } cancelMessage.setService(_service); cancelMessage.setType(Message::Type_Cancel); cancelMessage.setValue(AnyReference::from(originalMessageId), "I"); cancelMessage.setObject(_object); sock->send(cancelMessage); }
void Session_Service::onRemoteObjectComplete(qi::Future<void> future, long requestId) { qiLogDebug() << "Got metaobject"; boost::recursive_mutex::scoped_lock l(_requestsMutex); ServiceRequest *sr = serviceRequest(requestId); if (!sr) return; if (future.hasError()) { sr->promise.setError(future.error()); removeRequest(requestId); return; } { boost::recursive_mutex::scoped_lock sl(_remoteObjectsMutex); RemoteObjectMap::iterator it = _remoteObjects.find(sr->name); if (it != _remoteObjects.end()) { //another object have been registered before us, return it //the new socket will be closed when the request is deleted qiLogVerbose() << "A request for the service " << sr->name << " have been discarded, " << "the remoteobject on the service was already available."; sr->promise.setValue(it->second); } else { AnyObject o = makeDynamicAnyObject(sr->remoteObject); //register the remote object in the cache addService(sr->name, o); sr->promise.setValue(o); sr->remoteObject = 0; } } removeRequest(requestId); }
void TimelinePrivate::goTo(int pFrame) { qiLogDebug() << "goto timeline with : " << pFrame; boost::unique_lock<boost::recursive_mutex> lock(_methodMonitor); _currentFrame = pFrame; killMotionOrders(); update(); }
Node::Node(NodeKind kind, NodeType type, const Location& loc, const std::string& comment) : _kind(kind) , _type(type) , _loc(loc) , _comment(comment) { qiLogDebug() << "new node(" << _kind << ", " << _type << ")"; }
TEST(TestJSONDecoder, StructWithDifferentSizedFields) { Qiqi val; val.ffloat = 3.14f; val.fdouble = 5.5555; val.fint = 44; std::cout << qi::AnyValue(val).signature().toString() << std::endl; std::string json = qi::encodeJSON(val); qiLogDebug() << json << std::endl; qi::AnyValue res_any = qi::decodeJSON(json); qiLogDebug() << res_any.signature().toString() << std::endl; Qiqi res = res_any.to<Qiqi>(); EXPECT_EQ(val, res) << qi::encodeJSON(val) << "\n" << qi::encodeJSON(res); }
unsigned int Message::function() const { if (type() == Type_Event) { qiLogDebug() << "called function() on Type_Event message"; } return _p->header.action; }
size_t Buffer::read(void* buffer, size_t off, size_t length) const { if (!_p) { qiLogDebug("qi.buffer") << "read on empty buffer"; return -1; } if (off > _p->used) { qiLogDebug("qi.buffer") << "Attempt to read " << off+length <<" on buffer of size " << _p->used; return -1; } size_t copy = std::min(length, _p->used - off); memcpy(buffer, (char*)_p->data()+off, copy); return copy; }
unsigned int Message::event() const { if (type() != Type_Event) { qiLogDebug() << "called event() on non Type_Event message"; } return _p->header.action; }
/* Events can reach us in basically random orders. * Synchronous hierarchy is constructed when an event arrives (it places * itself as deep as it can, but only goes as a child if it is sures * the decision is correct), and when an event is completed (end time * received, then it recheck following events to check if they were * actually children of his). * * Async hierarchy is double-checked the same way: When a new event inserts * itself, it tries to steal async children from its parents. And it * inserts itself as async child of the best correct match * * * Assumes end event is reached after start event for same id */ static void insertTrace(CallList& l, CallData* d, CallData* parent = 0) { qiLogDebug() << "insertTrace " << d->uid; // Get first entry that started after us. CallList::iterator it = std::upper_bound(l.begin(), l.end(), CallTime(d)); qiLogDebug() << "upperBoud " << ((it == l.end())? -1 : (int)(*it)->uid) << ' ' << (it == l.begin()); if (it == l.begin()) { // We are first l.insert(it, d); d->parent = parent; } else { // try to steal async children from entry above us CallList::iterator iprev = it; --iprev; CallData* prev = *iprev; for (unsigned i=0; i<prev->asyncChildren.size(); ++i) { CallData* child = prev->asyncChildren[i]; if (child->tPost > d->tStart) { // This child was misplaced and is ours d->asyncChildren.push_back(child); prev->asyncChildren[i] = prev->asyncChildren.back(); prev->asyncChildren.pop_back(); child->asyncParent = d; } } // Check if we are child of it QI_ASSERT(prev->tStart <= d->tStart); if (prev->tEnd >= d->tStart) { qiLogDebug() << "Insert to child " << d->tStart << ' ' << d->tEnd << ' ' << prev->tStart << ' ' << prev->tEnd; insertTrace(prev->children, d, prev); // we start within it interval: we are its chilrend } else { l.insert(it, d); d->parent = parent; } } // if "it" was not completed, it will check following elements again // when tEnd will be available }
void Session_Service::addService(const std::string& name, const qi::AnyObject &obj) { boost::recursive_mutex::scoped_lock sl(_remoteObjectsMutex); RemoteObjectMap::iterator it = _remoteObjects.find(name); qiLogDebug() << "Adding remoteobject:" << name << " :" << &obj; if (it == _remoteObjects.end()) _remoteObjects[name] = obj; else throw std::runtime_error("Service already in cache: " + name); }
void Session_Service::onTransportSocketResult(qi::Future<TransportSocketPtr> value, long requestId) { qiLogDebug() << "Got transport socket for service"; { boost::recursive_mutex::scoped_lock sl(_requestsMutex); ServiceRequest *sr = serviceRequest(requestId); if (!sr) return; if (value.hasError()) { sr->promise.setError(value.error()); removeRequest(requestId); return; } } TransportSocketPtr socket = value.value(); // If true, this socket came from the socket cache and has already been identified. // This typically happens when two services are behind the same endpoint. // We forge a message that just shows we've authenticated successfully. if (socket->hasReceivedRemoteCapabilities()) { Message dummy; CapabilityMap cm; cm[AuthProvider::State_Key] = AnyValue::from(AuthProvider::State_Done); dummy.setType(Message::Type_Reply); dummy.setFunction(qi::Message::ServerFunction_Authenticate); dummy.setValue(AnyValue::from(cm), typeOf<CapabilityMap>()->signature()); onAuthentication(TransportSocket::SocketEventData(dummy), requestId, socket, ClientAuthenticatorPtr(new NullClientAuthenticator), SignalSubscriberPtr()); return; } ClientAuthenticatorPtr authenticator = _authFactory->newAuthenticator(); CapabilityMap authCaps; { CapabilityMap tmp = authenticator->initialAuthData(); for (CapabilityMap::iterator it = tmp.begin(), end = tmp.end(); it != end; ++it) authCaps[AuthProvider::UserAuthPrefix + it->first] = it->second; } SignalSubscriberPtr protSubscriber(new SignalSubscriber); *protSubscriber = socket->socketEvent.connect(&Session_Service::onAuthentication, this, _1, requestId, socket, authenticator, protSubscriber); Message msgCapabilities; msgCapabilities.setFunction(Message::ServerFunction_Authenticate); msgCapabilities.setService(Message::Service_Server); msgCapabilities.setType(Message::Type_Call); TransportSocketPtr sdSocket = _sdClient->socket(); CapabilityMap socketCaps; if (sdSocket) { socketCaps = sdSocket->localCapabilities(); socket->advertiseCapabilities(socketCaps); } socketCaps.insert(authCaps.begin(), authCaps.end()); msgCapabilities.setValue(socketCaps, typeOf<CapabilityMap>()->signature()); socket->send(msgCapabilities); }