diff options
| author | Trung Tran <trung.tran@ettus.com> | 2019-01-09 21:37:25 -0800 | 
|---|---|---|
| committer | Brent Stapleton <brent.stapleton@ettus.com> | 2019-01-14 09:43:56 -0800 | 
| commit | dceb0aef4000e5f37c5136c5b19436788d1feb2e (patch) | |
| tree | 25f779196ba3e6681d75a2375ed1439178a4a74e /host/lib/include/uhdlib | |
| parent | fd3f5d011fb99304402cbf3c1e8c596478316119 (diff) | |
| download | uhd-dceb0aef4000e5f37c5136c5b19436788d1feb2e.tar.gz uhd-dceb0aef4000e5f37c5136c5b19436788d1feb2e.tar.bz2 uhd-dceb0aef4000e5f37c5136c5b19436788d1feb2e.zip  | |
utils:rpc: set rpc timeout during rpc call
The current implementation of the UHD RPC client has a timeout that is
being accessed non-atomically. Many calls follow the pattern:
1. set_timeout(value)
2. request_rpc()
3. set_timeout(default)
which is not atomic. Other concurrent calls on the same rpc client may
change the timeout value; leads to unexpected behavior
These new set of function will, instead, handle
setting and re-setting the timeout atomically in the RPC request.
Diffstat (limited to 'host/lib/include/uhdlib')
| -rw-r--r-- | host/lib/include/uhdlib/utils/rpc.hpp | 123 | 
1 files changed, 120 insertions, 3 deletions
diff --git a/host/lib/include/uhdlib/utils/rpc.hpp b/host/lib/include/uhdlib/utils/rpc.hpp index c7c27afd2..f1cfec06b 100644 --- a/host/lib/include/uhdlib/utils/rpc.hpp +++ b/host/lib/include/uhdlib/utils/rpc.hpp @@ -12,9 +12,10 @@  #include <uhd/utils/log.hpp>  #include <uhd/exception.hpp>  #include <boost/format.hpp> - +constexpr uint64_t DEFAULT_RPC_TIMEOUT_MS = 2000;  namespace uhd { +  /*! Abstraction for RPC client   *   * Purpose of this class is to wrap the underlying RPC implementation. @@ -29,9 +30,10 @@ class rpc_client      static sptr make(              const std::string &addr,              const uint16_t port, +            const uint64_t timeout_ms = DEFAULT_RPC_TIMEOUT_MS,              const std::string &get_last_error_cmd=""      ) { -        return std::make_shared<rpc_client>(addr, port, get_last_error_cmd); +        return std::make_shared<rpc_client>(addr, port, timeout_ms, get_last_error_cmd);      }      /*! @@ -45,10 +47,13 @@ class rpc_client      rpc_client(              const std::string &addr,              const uint16_t port, +            const uint64_t timeout_ms = DEFAULT_RPC_TIMEOUT_MS,              std::string const &get_last_error_cmd=""      ) : _client(addr, port)        , _get_last_error_cmd(get_last_error_cmd) +      , _default_timeout_ms(timeout_ms)      { +        _client.set_timeout(_default_timeout_ms);          // nop      } @@ -86,11 +91,84 @@ class rpc_client          }      }; +     /*! Perform an RPC request. +     * +     * Thread safe (locked). This function blocks until it receives a valid +     * response from the server. +     * +     * \param timeout_ms is time limit for this RPC call. +     * \param func_name The function name that is called via RPC +     * \param args All these arguments are passed to the RPC call +     * +     * \throws uhd::runtime_error in case of failure +     */ +    template <typename return_type, typename... Args> +    return_type request(uint64_t timeout_ms, std::string const& func_name, Args&&... args) +    { +        std::lock_guard<std::mutex> lock(_mutex); +        auto holder = rpcc_timeout_holder(&_client, timeout_ms, _default_timeout_ms); +        try { +            return _client.call(func_name, std::forward<Args>(args)...) +                .template as<return_type>(); +        } catch (const ::rpc::rpc_error &ex) { +            const std::string error = _get_last_error_safe(); +            if (not error.empty()) { +                UHD_LOG_ERROR("RPC", error); +            } +            throw uhd::runtime_error(str( +                boost::format("Error during RPC call to `%s'. Error message: %s") +                % func_name % (error.empty() ? ex.what() : error) +            )); +        } catch (const std::bad_cast& ex) { +            throw uhd::runtime_error(str( +                boost::format("Error during RPC call to `%s'. Error message: %s") +                % func_name % ex.what() +            )); +        } +    }; + +      /*! Perform an RPC notification.       *       * Thread safe (locked). This function does not require a response from the       * server, although the underlying implementation may provide one.       * +     * \param timeout_ms is time limit for this RPC call. +     * \param func_name The function name that is called via RPC +     * \param args All these arguments are passed to the RPC call +     * +     * \throws uhd::runtime_error in case of failure +     */ +    template <typename... Args> +    void notify(uint64_t timeout_ms, std::string const& func_name, Args&&... args) +    { +        std::lock_guard<std::mutex> lock(_mutex); +        auto holder = rpcc_timeout_holder(&_client, timeout_ms, _default_timeout_ms); +        try { + +            _client.call(func_name, std::forward<Args>(args)...); +        } catch (const ::rpc::rpc_error &ex) { +            const std::string error = _get_last_error_safe(); +            if (not error.empty()) { +                UHD_LOG_ERROR("RPC", error); +            } +            throw uhd::runtime_error(str( +                boost::format("Error during RPC call to `%s'. Error message: %s") +                % func_name % (error.empty() ? ex.what() : error) +            )); +        } catch (const std::bad_cast& ex) { +            throw uhd::runtime_error(str( +                boost::format("Error during RPC call to `%s'. Error message: %s") +                % func_name % ex.what() +            )); +        } +    }; + +     /*! Perform an RPC notification. +     * +     * Thread safe (locked). This function does not require a response from the +     * server, although the underlying implementation may provide one. +     *       * \param func_name The function name that is called via RPC       * \param args All these arguments are passed to the RPC call       * @@ -130,6 +208,14 @@ class rpc_client          return request<return_type>(func_name, _token, std::forward<Args>(args)...);      }; +    /*! Like request_with_token(), but it can be specified different timeout than default. +     */ +    template <typename return_type, typename... Args> +    return_type request_with_token(uint64_t timeout_ms, std::string const& func_name, Args&&... args) +    { +        return request<return_type>(timeout_ms, func_name, _token, std::forward<Args>(args)...); +    }; +      /*! Like notify(), also provides a token.       *       * This is a convenience wrapper to directly call a function that requires @@ -141,6 +227,14 @@ class rpc_client          notify(func_name, _token, std::forward<Args>(args)...);      }; +     /*! Like notify_with_token() but it can be specified different timeout than default. +     */ +    template <typename... Args> +    void notify_with_token(uint64_t timeout_ms, std::string const& func_name, Args&&... args) +    { +        notify(timeout_ms, func_name, _token, std::forward<Args>(args)...); +    }; +      /*! Sets the token value. This is used by the `_with_token` methods.       */      void set_token(const std::string &token) @@ -154,6 +248,29 @@ class rpc_client      }    private: + +    /*! This is internal object to hold timeout of the rpc client +     * it is used as an RAII in code block. +     */ +    class rpcc_timeout_holder{ +        public: + +            rpcc_timeout_holder(::rpc::client *client, +                uint64_t set_timeout, +                uint64_t resume_timeout +            ): _rpcc(client), _save_timeout(resume_timeout) +            { +                _rpcc->set_timeout(set_timeout); +            } + +            ~rpcc_timeout_holder(){ +                _rpcc->set_timeout(_save_timeout); +            } +        private: +            ::rpc::client *_rpcc; +            uint64_t _save_timeout; +    }; +       /*! Pull the last error out of the RPC server. Not thread-safe, meant to        * be called from notify() or request().        * @@ -181,7 +298,7 @@ class rpc_client      ::rpc::client _client;      //! If set, this is the command that will retrieve an error      const std::string _get_last_error_cmd; - +    uint64_t _default_timeout_ms;      std::string _token;      std::mutex _mutex;  };  | 
