// We received a ServiceInfo, and want to establish a connection void Session_Service::onServiceInfoResult(qi::Future<qi::ServiceInfo> result, long requestId, std::string protocol) { qiLogDebug() << "Got serviceinfo message"; { boost::recursive_mutex::scoped_lock sl(_requestsMutex); ServiceRequest *sr = serviceRequest(requestId); if (!sr) return; if (result.hasError()) { sr->promise.setError(result.error()); removeRequest(requestId); return; } const qi::ServiceInfo &si = result.value(); sr->serviceId = si.serviceId(); if (_sdClient->isLocal()) { // Wait! If sd is local, we necessarily have an open socket // on which service was registered, whose lifetime is bound // to the service TransportSocketPtr s = _sdClient->_socketOfService(sr->serviceId); if (!s) // weird qiLogVerbose() << "_socketOfService returned 0"; else { // check if the socket support that capability if (s->remoteCapability("ClientServerSocket", false)) { qiLogVerbose() << "sd is local and service is capable, going through socketOfService"; onTransportSocketResult(qi::Future<TransportSocketPtr>(s), requestId); return; } } } //empty serviceInfo if (!si.endpoints().size()) { std::stringstream ss; ss << "No endpoints returned for service:" << sr->name << " (id:" << sr->serviceId << ")"; qiLogVerbose() << ss.str(); sr->promise.setError(ss.str()); removeRequest(requestId); return; } if (protocol != "") { std::vector<qi::Url>::const_iterator it = si.endpoints().begin(); for (; it != si.endpoints().end() && it->protocol() != protocol; it++) { continue; } if (it == si.endpoints().end()) { std::stringstream ss; ss << "No " << protocol << " endpoint available for service:" << sr->name << " (id:" << sr->serviceId << ")"; qiLogVerbose() << ss.str(); sr->promise.setError(ss.str()); } } } qiLogDebug() << "Requesting socket from cache"; qi::Future<qi::TransportSocketPtr> fut = _socketCache->socket(result.value(), protocol); fut.connect(&Session_Service::onTransportSocketResult, this, _1, requestId); }
qi::Future<AnyReference> RemoteObject::metaCall(AnyObject, unsigned int method, const qi::GenericFunctionParameters &in, MetaCallType callType, Signature returnSignature) { MetaMethod *mm = metaObject().method(method); if (!mm) { std::stringstream ss; ss << "Method " << method << " not found on service " << _service; return makeFutureError<AnyReference>(ss.str()); } float canConvert = 1; if (returnSignature.isValid()) { canConvert = mm->returnSignature().isConvertibleTo(returnSignature); qiLogDebug() << "return type conversion score: " << canConvert; if (canConvert == 0) { // last chance for dynamics and adventurous users canConvert = returnSignature.isConvertibleTo(mm->returnSignature()); if (canConvert == 0) return makeFutureError<AnyReference>( "Call error: will not be able to convert return type from " + mm->returnSignature().toString() + " to " + returnSignature.toString()); else qiLogVerbose() << "Return signature might be incorrect depending on the value, from " + mm->returnSignature().toString() + " to " + returnSignature.toString(); } } /* The promise will be set: - From here in case of error - From a network callback, called asynchronously in thread pool So it is safe to use a sync promise. */ qi::Promise<AnyReference> out(&PromiseNoop<AnyReference>, FutureCallbackType_Sync); qi::Message msg; TransportSocketPtr sock; // qiLogDebug() << this << " metacall " << msg.service() << " " << msg.function() <<" " << msg.id(); { boost::mutex::scoped_lock lock(_socketMutex); boost::mutex::scoped_lock lock2(_promisesMutex); // Check socket while holding the lock to avoid a race with close() // where we would add a promise to the map after said map got cleared if (!_socket || !_socket->isConnected()) { return makeFutureError<AnyReference>("Socket is not connected"); } // The remote object can be concurrently closed / other operation that modifies _socket // (even set it to null). We store the current socket locally so that the behavior // of metacall stays consistent throughout the function's execution. sock = _socket; if (_promises.find(msg.id()) != _promises.end()) { qiLogError() << "There is already a pending promise with id " << msg.id(); } qiLogDebug() << "Adding promise id:" << msg.id(); _promises[msg.id()] = out; } qi::Signature funcSig = mm->parametersSignature(); try { msg.setValues(in, funcSig, this, sock.get()); } catch(const std::exception& e) { qiLogVerbose() << "setValues exception: " << e.what(); if (!sock->remoteCapability("MessageFlags", false)) throw e; // Delegate conversion to the remote end. msg.addFlags(Message::TypeFlag_DynamicPayload); msg.setValues(in, "m", this, sock.get()); } if (canConvert < 0.2) { msg.addFlags(Message::TypeFlag_ReturnType); msg.setValue(returnSignature.toString(), Signature("s")); } msg.setType(qi::Message::Type_Call); msg.setService(_service); msg.setObject(_object); msg.setFunction(method); //error will come back as a error message if (!sock->isConnected() || !sock->send(msg)) { qi::MetaMethod* meth = metaObject().method(method); std::stringstream ss; if (meth) { ss << "Network error while sending data to method: '"; ss << meth->toString(); ss << "'."; } else { ss << "Network error while sending data an unknown method (id=" << method << ")."; } if (!sock->isConnected()) { ss << " Socket is not connected."; qiLogVerbose() << ss.str(); } else { qiLogError() << ss.str(); } out.setError(ss.str()); { boost::mutex::scoped_lock lock(_promisesMutex); qiLogDebug() << "Removing promise id:" << msg.id(); _promises.erase(msg.id()); } } else out.setOnCancel(qi::bind(&RemoteObject::onFutureCancelled, this, msg.id())); return out.future(); }