25#ifndef GOBY_ZEROMQ_TRANSPORT_INTERPROCESS_H
26#define GOBY_ZEROMQ_TRANSPORT_INTERPROCESS_H
32#include <condition_variable>
40#include <unordered_map>
64#if ZMQ_VERSION <= ZMQ_MAKE_VERSION(4, 3, 1)
65#define USE_OLD_ZMQ_CPP_API
68#if CPPZMQ_VERSION < ZMQ_MAKE_VERSION(4, 7, 1)
69#define USE_OLD_CPPZMQ_SETSOCKOPT
72#if CPPZMQ_VERSION < ZMQ_MAKE_VERSION(4, 8, 0)
73#define USE_OLD_CPPZMQ_POLL
80template <
typename Data>
class Publisher;
95#ifdef USE_OLD_ZMQ_CPP_API
110#ifdef USE_OLD_CPPZMQ_SETSOCKOPT
111 control_socket_.setsockopt(ZMQ_LINGER, 0);
112 publish_socket_.setsockopt(ZMQ_LINGER, 0);
114 control_socket_.set(zmq::sockopt::linger, 0);
115 publish_socket_.set(zmq::sockopt::linger, 0);
129 void publish(
const std::string& identifier,
const char* bytes,
int size,
130 bool ignore_buffer =
false);
135 std::deque<protobuf::InprocControl>&
control_buffer() {
return control_buffer_; }
140 zmq::socket_t control_socket_;
141 zmq::socket_t publish_socket_;
143 bool have_pubsub_sockets_{
false};
145 std::deque<std::pair<std::string, std::vector<char>>>
149 std::deque<protobuf::InprocControl> control_buffer_;
157 zmq::context_t& context, std::atomic<bool>& alive,
158 std::shared_ptr<std::condition_variable> poller_cv);
162#ifdef USE_OLD_CPPZMQ_SETSOCKOPT
163 control_socket_.setsockopt(ZMQ_LINGER, 0);
164 subscribe_socket_.setsockopt(ZMQ_LINGER, 0);
165 manager_socket_.setsockopt(ZMQ_LINGER, 0);
167 control_socket_.set(zmq::sockopt::linger, 0);
168 subscribe_socket_.set(zmq::sockopt::linger, 0);
169 manager_socket_.set(zmq::sockopt::linger, 0);
174 void poll(
long timeout_ms = -1);
175 void control_data(
const zmq::message_t& zmq_msg);
176 void subscribe_data(
const zmq::message_t& zmq_msg);
177 void manager_data(
const zmq::message_t& zmq_msg);
183 zmq::socket_t control_socket_;
184 zmq::socket_t subscribe_socket_;
185 zmq::socket_t manager_socket_;
186 std::atomic<bool>& alive_;
187 std::shared_ptr<std::condition_variable> poller_cv_;
188 std::vector<zmq::pollitem_t> poll_items_;
199 bool have_pubsub_sockets_{
false};
201 bool manager_waiting_for_reply_{
false};
206 std::chrono::milliseconds(100)};
209template <
typename InnerTransporter,
210 template <
typename Derived,
typename InnerTransporterType,
211 typename ImplementationTag_>
class PortalBase,
212 typename ImplementationTag>
214 :
public PortalBase<InterProcessPortalImplementation<InnerTransporter, PortalBase,
216 InnerTransporter, ImplementationTag>
221 InnerTransporter, ImplementationTag>;
226 zmq_context_(cfg.zeromq_number_io_threads()),
227 zmq_main_(zmq_context_),
237 zmq_context_(cfg.zeromq_number_io_threads()),
238 zmq_main_(zmq_context_),
248 zmq_main_.reader_shutdown();
269 zmq_thread_ = std::make_unique<std::thread>([
this]() { zmq_read_thread_.run(); });
271 while (!zmq_main_.subscribe_ready())
274 if (zmq_main_.recv(&control_msg))
276 switch (control_msg.
type())
292 [
this](std::shared_ptr<const protobuf::ManagerResponse> response)
295 << response->ShortDebugString() << std::endl;
297 response->client_pid() == getpid() &&
298 response->client_name() == cfg_.client_name())
300 zmq_main_.set_hold_state(response->hold());
304 if (zmq_main_.publish_ready())
315 void _do_publish(
const std::string& identifier,
const std::vector<char>& bytes)
317 zmq_main_.publish(identifier, bytes.data(), bytes.size(), ignore_buffer_);
320 void _do_portal_subscribe(
const std::string& identifier) { zmq_main_.subscribe(identifier); }
321 void _do_portal_unsubscribe(
const std::string& identifier)
323 zmq_main_.unsubscribe(identifier);
326 void _do_portal_wildcard_subscribe() { zmq_main_.subscribe(delimiter_str); }
327 void _do_portal_wildcard_unsubscribe() { zmq_main_.unsubscribe(delimiter_str); }
329 int _poll(std::unique_ptr<std::unique_lock<std::mutex>>& lock)
332 protobuf::InprocControl new_control_msg;
334#ifdef USE_OLD_ZMQ_CPP_API
335 int flags = ZMQ_NOBLOCK;
337 auto flags = zmq::recv_flags::dontwait;
340 while (zmq_main_.recv(&new_control_msg, flags))
341 zmq_main_.control_buffer().push_back(new_control_msg);
343 while (!zmq_main_.control_buffer().empty())
345 const auto& control_msg = zmq_main_.control_buffer().front();
346 switch (control_msg.type())
348 case protobuf::InprocControl::RECEIVE:
351 this->_handle_received_data(lock, control_msg.received_data());
355 case protobuf::InprocControl::REQUEST_HOLD_STATE:
357 protobuf::ManagerRequest req;
359 req.set_ready(ready_);
360 req.set_request(protobuf::PROVIDE_HOLD_STATE);
361 req.set_client_name(cfg_.client_name());
362 req.set_client_pid(getpid());
365 << req.ShortDebugString() << std::endl;
367 ignore_buffer_ =
true;
368 this->
template publish<groups::manager_request>(req);
369 ignore_buffer_ =
false;
375 zmq_main_.control_buffer().pop_front();
381 const protobuf::InterProcessPortalConfig cfg_;
383 std::unique_ptr<std::thread> zmq_thread_;
384 std::atomic<bool> zmq_alive_{
true};
385 zmq::context_t zmq_context_;
386 InterProcessPortalMainThread zmq_main_;
387 InterProcessPortalReadThread zmq_read_thread_;
390 bool ignore_buffer_{
false};
397 : context_(context), cfg_(cfg)
412 zmq::context_t& context_;
424 :
Manager(context, cfg, router)
426 for (
const auto& req_c : hold.
required_client()) required_clients_.insert(req_c);
438 std::set<std::string> reported_clients_;
439 std::set<std::string> required_clients_;
441 zmq::context_t& context_;
445 std::vector<zmq::pollitem_t> poll_items_;
449 SOCKET_SUBSCRIBE = 1,
456 std::unique_ptr<zmq::socket_t> manager_socket_;
457 std::unique_ptr<zmq::socket_t> subscribe_socket_;
458 std::unique_ptr<zmq::socket_t> publish_socket_;
466 std::string zmq_filter_rep_{
468 middleware::SerializerParserHelper<
469 protobuf::ManagerResponse,
476template <
typename InnerTransporter = m
iddleware::NullTransporter>
481template <
typename InnerTransporter = m
iddleware::NullTransporter>
Class for grouping publications in the Goby middleware. Analogous to "topics" in ROS,...
InnerTransporter & inner()
InterProcessPortalCommon< Derived, InnerTransporter > Common
InterModuleTransporterBase< Derived, InnerTransporter, ImplementationTag > Base
Implements the forwarder concept for the interprocess layer.
static std::string make_identifier(const std::string &type_name, int scheme, const std::string &group, IdentifierWildcard wildcard, const std::string &process, std::unordered_map< int, std::string > *schemes_buffer=nullptr, std::unordered_map< std::thread::id, std::string > *threads_buffer=nullptr)
static const char end_delimiter
void _subscribe(std::function< void(std::shared_ptr< const Data > d)> f, const goby::middleware::Group &group, const middleware::Subscriber< Data > &)
void _unsubscribe(const goby::middleware::Group &group, const middleware::Subscriber< Data > &=middleware::Subscriber< Data >())
PollerInterface(std::shared_ptr< std::mutex > poll_mutex, std::shared_ptr< std::condition_variable > cv)
std::shared_ptr< std::condition_variable > cv()
access the condition variable used for poll synchronization
Class that holds additional metadata and callback functions related to a subscription (and is optiona...
bool hold_state()
When using hold functionality, returns whether the system is holding (true) and thus waiting for all ...
middleware::IdentifierWildcard IdentifierWildcard
~InterProcessPortalImplementation()
InterProcessPortalImplementation(InnerTransporter &inner, const protobuf::InterProcessPortalConfig &cfg)
InterProcessPortalImplementation(const protobuf::InterProcessPortalConfig &cfg)
middleware::InterModulePortalBase< InterProcessPortalImplementation< InnerTransporter, middleware::InterModulePortalBase, detail::InterModuleTag >, InnerTransporter, detail::InterModuleTag > Base
void ready()
When using hold functionality, call when the process is ready to receive publications (typically done...
void set_publish_cfg(const protobuf::Socket &cfg)
~InterProcessPortalMainThread()
void unsubscribe(const std::string &identifier)
std::deque< protobuf::InprocControl > & control_buffer()
InterProcessPortalMainThread(zmq::context_t &context)
void subscribe(const std::string &identifier)
void publish(const std::string &identifier, const char *bytes, int size, bool ignore_buffer=false)
void send_control_msg(const protobuf::InprocControl &control)
bool recv(protobuf::InprocControl *control_msg, zmq_recv_flags_type flags=zmq_recv_flags_type())
void set_hold_state(bool hold)
InterProcessPortalReadThread(const protobuf::InterProcessPortalConfig &cfg, zmq::context_t &context, std::atomic< bool > &alive, std::shared_ptr< std::condition_variable > poller_cv)
~InterProcessPortalReadThread()
Manager(zmq::context_t &context, const protobuf::InterProcessPortalConfig &cfg, const Router &router, const protobuf::InterProcessManagerHold &hold)
protobuf::Socket subscribe_socket_cfg()
Manager(zmq::context_t &context, const protobuf::InterProcessPortalConfig &cfg, const Router &router)
protobuf::Socket publish_socket_cfg()
protobuf::ManagerResponse handle_request(const protobuf::ManagerRequest &pb_request)
Router & operator=(Router &)=delete
std::atomic< unsigned > sub_port
unsigned last_port(zmq::socket_t &socket)
Router(zmq::context_t &context, const protobuf::InterProcessPortalConfig &cfg)
std::atomic< unsigned > pub_port
::goby::zeromq::protobuf::InprocControl_InprocControlType type() const
static constexpr InprocControlType PUB_CONFIGURATION
const ::goby::zeromq::protobuf::Socket & publish_socket() const
const std::string & required_client(int index) const
Objects implementing the Goby nested middleware.
constexpr int scheme()
Placeholder to provide an interface for the scheme() function family.
@ PROCESS_THREAD_WILDCARD
constexpr goby::middleware::Group manager_response
constexpr goby::middleware::Group manager_request
constexpr const char * delimiter_str
InterProcessPortalImplementation< InnerTransporter, middleware::InterProcessPortalBase, detail::InterProcessTag > InterProcessPortal
void setup_socket(zmq::socket_t &socket, const protobuf::Socket &cfg)
middleware::InterProcessForwarder< InnerTransporter, detail::InterProcessTag > InterProcessForwarder
The global namespace for the Goby project.
util::FlexOstream glog
Access the Goby logger through this object.
Class for parsing and serializing a given marshalling scheme. Must be specialized for a particular sc...
std::chrono::time_point< SystemClock > time_point
static time_point now() noexcept
Returns the current system time unless SimulatorSettings::using_sim_time is set to true,...
std::chrono::microseconds duration
Duration type.
ImplementationTag for zeromq interprocess transporters.