// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma // de Barcelona (UAB). // // This work is licensed under the terms of the MIT license. // For a copy, see . #pragma once #include "carla/MoveHandler.h" #include "carla/Time.h" #include "carla/rpc/Metadata.h" #include "carla/rpc/Response.h" #include #include #include #include namespace carla { namespace rpc { // =========================================================================== // -- Server ----------------------------------------------------------------- // =========================================================================== /// An RPC server in which functions can be bind to run synchronously or /// asynchronously. /// /// Use `AsyncRun` to start the worker threads, and use `SyncRunFor` to /// run a slice of work in the caller's thread. /// /// Functions that are bind using `BindAsync` will run asynchronously in the /// worker threads. Functions that are bind using `BindSync` will run within /// `SyncRunFor` function. class Server { public: template explicit Server(Args &&... args); template void BindSync(const std::string &name, FunctorT &&functor); template void BindAsync(const std::string &name, FunctorT &&functor); void AsyncRun(size_t worker_threads) { _server.async_run(worker_threads); } void SyncRunFor(time_duration duration) { #ifdef LIBCARLA_INCLUDED_FROM_UE4 #include TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__); #include #endif // LIBCARLA_INCLUDED_FROM_UE4 _sync_io_context.reset(); _sync_io_context.run_for(duration.to_chrono()); } /// @warning does not stop the game thread. void Stop() { _server.stop(); } private: boost::asio::io_context _sync_io_context; ::rpc::server _server; }; // =========================================================================== // -- Server implementation -------------------------------------------------- // =========================================================================== namespace detail { template struct FunctionWrapper : FunctionWrapper {}; template struct FunctionWrapper : FunctionWrapper {}; template struct FunctionWrapper : FunctionWrapper {}; template struct FunctionWrapper : public FunctionWrapper {}; template struct FunctionWrapper : public FunctionWrapper {}; template struct FunctionWrapper { /// Wraps @a functor into a function type with equivalent signature. The /// wrap function returned. When called, posts @a functor into the /// io_context; if the client called this method synchronously, waits for /// the posted task to finish, otherwise returns immediately. /// /// This way, no matter from which thread the wrap function is called, the /// @a functor provided is always called from the context of the io_context. /// I.e., we can use the io_context to run tasks on a specific thread (e.g. /// game thread). template static auto WrapSyncCall(boost::asio::io_context &io, FuncT &&functor) { return [&io, functor=std::forward(functor)](Metadata metadata, Args... args) -> R { auto task = std::packaged_task([functor=std::move(functor), args...]() { return functor(args...); }); if (metadata.IsResponseIgnored()) { // Post task and ignore result. boost::asio::post(io, MoveHandler(task)); return R(); } else { // Post task and wait for result. auto result = task.get_future(); boost::asio::post(io, MoveHandler(task)); return result.get(); } }; } /// Wraps @a functor into a function type with equivalent signature that /// handles the metadata sent by the client. If the client called this /// method asynchronously, the result is ignored. template static auto WrapAsyncCall(FuncT &&functor) { return [functor=std::forward(functor)](::carla::rpc::Metadata metadata, Args... args) -> R { if (metadata.IsResponseIgnored()) { functor(args...); return R(); } else { return functor(args...); } }; } }; } // namespace detail template inline Server::Server(Args && ... args) : _server(std::forward(args) ...) { _server.suppress_exceptions(true); } template inline void Server::BindSync(const std::string &name, FunctorT &&functor) { using Wrapper = detail::FunctionWrapper; _server.bind( name, Wrapper::WrapSyncCall(_sync_io_context, std::forward(functor))); } template inline void Server::BindAsync(const std::string &name, FunctorT &&functor) { using Wrapper = detail::FunctionWrapper; _server.bind( name, Wrapper::WrapAsyncCall(std::forward(functor))); } } // namespace rpc } // namespace carla