예제 #1
0
  qi::Future<void> RemoteObject::metaDisconnect(SignalLink linkId)
  {
    unsigned int event = linkId >> 32;
    //disconnect locally
    qi::Future<void> fut = DynamicObject::metaDisconnect(linkId);
    if (fut.hasError())
    {
      std::stringstream ss;
      ss << "Disconnection failure for " << linkId << ", error:" << fut.error();
      qiLogWarning() << ss.str();
      return qi::makeFutureError<void>(ss.str());
    }

    boost::recursive_mutex::scoped_lock _lock(_localToRemoteSignalLinkMutex);
    LocalToRemoteSignalLinkMap::iterator it;
    it = _localToRemoteSignalLink.find(event);
    if (it == _localToRemoteSignalLink.end()) {
      qiLogWarning() << "Cant find " << event << " in the localtoremote signal map";
      return fut;
    }
    qi::SignalLink toDisco = qi::SignalBase::invalidSignalLink;
    {
      RemoteSignalLinks &rsl = it->second;
      std::vector<SignalLink>::iterator vslit;
      vslit = std::find(rsl.localSignalLink.begin(), rsl.localSignalLink.end(), linkId);

      if (vslit != rsl.localSignalLink.end()) {
        rsl.localSignalLink.erase(vslit);
      } else {
        qiLogWarning() << "Cant find " << linkId << " in the remote signal vector (event:" << event << ")";
      }

      //only drop the remote connection when no more local connection are registered
      if (rsl.localSignalLink.size() == 0) {
        toDisco = rsl.remoteSignalLink;
        _localToRemoteSignalLink.erase(it);
      }
    }
    if (toDisco != qi::SignalBase::invalidSignalLink) {
      TransportSocketPtr sock = _socket;
      if (sock && sock->isConnected())
        return _self.async<void>("unregisterEvent", _service, event, toDisco);
    }
    return fut;
  }
예제 #2
0
  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();
  }