Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Messenger.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * PB, Patrick Bos, Netherlands eScience Center, p.bos@esciencecenter.nl
5 * IP, Inti Pelupessy, Netherlands eScience Center, i.pelupessy@esciencecenter.nl
6 *
7 * Copyright (c) 2021, CERN
8 *
9 * Redistribution and use in source and binary forms,
10 * with or without modification, are permitted according to the terms
11 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
12 */
13
16
17#include <iostream>
18#include <stdexcept>
19
20namespace RooFit {
21namespace MultiProcess {
22
23/** \class Messenger
24 *
25 * \brief Manages the interprocess communication channels and wraps send and receive calls
26 *
27 * This class is used for all interprocess communication between the master,
28 * queue and worker processes. The communication runs over pipes built on
29 * socketpair(), which are created in the ProcessManager before forking, so
30 * that all processes inherit their ends of the connected channels; see
31 * Channel for the wire format.
32 *
33 * Several channels connect the processes for different purposes:
34 * - The master and queue processes share a channel that is mainly used for
35 * sending tasks to the queue from master.
36 * - The queue process shares a channel with each worker process. These are
37 * used by the workers to obtain tasks from the queue.
38 * - The master shares a channel with each worker process. The master -> worker
39 * direction carries state updates (previously published over a ZeroMQ
40 * PUB-SUB socket) and the worker -> master direction carries back task
41 * results, which the master receives in 'JobManager::retrieve()'.
42 *
43 * @param process_manager ProcessManager instance which manages the master,
44 * queue and worker processes that we want to set up
45 * communication for in this Messenger.
46 */
47
49{
50 // Claim the channel ends for this process type from the ProcessManager,
51 // which created them before forking. The channels are connected from
52 // birth, so no connection handshake is necessary.
53 if (process_manager.is_master()) {
54 mq_ = Channel{process_manager.claim_mq_fd()};
55 mw_.reserve(process_manager.N_workers());
56 for (std::size_t ix = 0; ix < process_manager.N_workers(); ++ix) {
57 mw_.emplace_back(process_manager.claim_mw_fd(ix));
58 }
59 for (auto &channel : mw_) {
61 }
62 } else if (process_manager.is_queue()) {
63 mq_ = Channel{process_manager.claim_mq_fd()};
64 qw_.reserve(process_manager.N_workers());
65 for (std::size_t ix = 0; ix < process_manager.N_workers(); ++ix) {
66 qw_.emplace_back(process_manager.claim_qw_fd(ix));
67 }
68 } else if (process_manager.is_worker()) {
69 this_worker_qw_ = Channel{process_manager.claim_qw_fd(process_manager.worker_id())};
70 this_worker_mw_ = Channel{process_manager.claim_mw_fd(process_manager.worker_id())};
71 } else {
72 // should never get here
73 throw std::runtime_error("Messenger ctor: I'm neither master, nor queue, nor a worker");
74 }
75}
76
77Messenger::~Messenger() = default;
78
80{
81 switch (snd_pipe) {
84 break;
85 }
88 break;
89 }
92 break;
93 }
96 break;
97 }
98 }
99}
100
102{
104
105 try {
106 switch (rcv_pipe) {
109 break;
110 }
113 break;
114 }
117 break;
118 }
121 break;
122 }
123 }
124 } catch (ppoll_error_t &) {
125 throw std::runtime_error("SIGTERM received in test_receive, aborting\n");
126 }
127
129 throw std::runtime_error(
130 "Messenger::test_connections: RECEIVE over connection failed, did not receive expected value!");
131 }
132}
133
134/// \brief Test whether the channels between all processes are working
135///
136/// \param process_manager ProcessManager object used to instantiate this object. Used to identify which process we are
137/// running on and hence which channels need to be tested.
138void Messenger::test_connections(const ProcessManager &process_manager)
139{
140 if (process_manager.is_master()) {
144 // make sure to always receive last on master, so that master knows when queue is done,
145 // which means workers are done as well, so if master is done everything is done:
147 } else if (process_manager.is_queue()) {
149 std::size_t mq_index;
150 std::tie(poller, mq_index) = create_queue_poller();
151
152 for (std::size_t ix = 0; ix < process_manager.N_workers(); ++ix) {
154 }
156
157 while (!process_manager.sigterm_received() && (poller.size() > 0)) {
158 // poll: wait until status change (-1: infinite timeout)
159 std::vector<std::size_t> poll_result;
160 bool abort;
161 std::tie(poll_result, abort) = careful_poll(poller);
162 if (abort)
163 break;
164
165 // then process incoming messages from the channels
166 for (auto readable_index : poll_result) {
167 // message comes from the master/queue channel (first element):
168 if (readable_index == mq_index) {
171 poller.unregister_channel(mq_);
172 } else { // from a worker channel
173 auto this_worker_id = readable_index - 1; // by construction of the queue poller
177
178 poller.unregister_channel(qw_[this_worker_id]);
179 }
180 }
181 }
183
184 } else if (process_manager.is_worker()) {
189 } else {
190 // should never get here
191 throw std::runtime_error("Messenger::test_connections: I'm neither master, nor queue, nor a worker");
192 }
193}
194
195/// Helper function that creates a poller for Queue::loop()
196std::pair<Poller, std::size_t> Messenger::create_queue_poller()
197{
199 std::size_t mq_index = poller.register_channel(mq_);
200 for (auto &channel : qw_) {
201 poller.register_channel(channel);
202 }
203 return {std::move(poller), mq_index};
204}
205
206/// Helper function that creates a poller for worker_loop()
207std::pair<Poller, std::size_t> Messenger::create_worker_poller()
208{
210 poller.register_channel(this_worker_qw_);
211 std::size_t mw_index = poller.register_channel(this_worker_mw_);
212 return {std::move(poller), mw_index};
213}
214
216{
217 // continue receiving the parts of an in-progress multipart message from
218 // the same worker (multipart messages must arrive as one unit, like with
219 // the ZeroMQ sockets used before)
220 if (mw_current_source_ != nullptr) {
221 return *mw_current_source_;
222 }
223 auto readable = mw_poller_.poll(-1);
224 // rotate over the workers for fairness, like a ZeroMQ PULL socket would
225 for (std::size_t offset = 0; offset < mw_.size(); ++offset) {
226 std::size_t candidate = (mw_next_poll_position_ + offset) % mw_.size();
227 for (std::size_t index : readable) {
228 if (index == candidate) {
229 mw_next_poll_position_ = (candidate + 1) % mw_.size();
230 return mw_[candidate];
231 }
232 }
233 }
234 // cannot happen: poll(-1) always returns at least one readable channel
235 throw std::logic_error("Messenger::select_worker_channel_on_master: poll returned no readable channels");
236}
237
239{
240 mw_current_source_ = more ? &channel : nullptr;
241}
242
243// -- WORKER - QUEUE COMMUNICATION --
244
246
247void Messenger::send_from_queue_to_worker(std::size_t /*this_worker_id*/) {}
248
249// -- QUEUE - MASTER COMMUNICATION --
250
252
254
255// for debugging
256#define PROCESS_VAL(p) \
257 case (p): s = #p; break;
258
259std::ostream &operator<<(std::ostream &out, const M2Q value)
260{
261 std::string s;
262 switch (value) {
264 default: s = std::to_string(static_cast<int>(value));
265 }
266 return out << s;
267}
268
269std::ostream &operator<<(std::ostream &out, const W2Q value)
270{
271 std::string s;
272 switch (value) {
274 default: s = std::to_string(static_cast<int>(value));
275 }
276 return out << s;
277}
278
279std::ostream &operator<<(std::ostream &out, const Q2W value)
280{
281 std::string s;
282 switch (value) {
285 default: s = std::to_string(static_cast<int>(value));
286 }
287 return out << s;
288}
289
290std::ostream &operator<<(std::ostream &out, const X2X value)
291{
292 std::string s;
293 switch (value) {
296 default: s = std::to_string(static_cast<int>(value));
297 }
298 return out << s;
299}
300
301#undef PROCESS_VAL
302
303/// Function called from send and receive template functions in debug builds
304/// used to monitor the messages that are going to be sent or are received.
305/// By defining this in the implementation file, compilation is a lot faster
306/// during debugging of Messenger or communication protocols.
307void Messenger::debug_print(std::string /*s*/)
308{
309 // print 's' when debugging
310}
311
312} // namespace MultiProcess
313} // namespace RooFit
#define PROCESS_VAL(p)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
One endpoint of a full-duplex interprocess message pipe.
Definition Channel.h:55
void test_receive(X2X expected_ping_value, test_rcv_pipes rcv_pipe, std::size_t worker_id)
Channel & select_worker_channel_on_master()
On master: pick the worker channel to receive the next message from.
void test_connections(const ProcessManager &process_manager)
Test whether the channels between all processes are working.
std::pair< Poller, std::size_t > create_worker_poller()
Helper function that creates a poller for worker_loop()
Messenger(ProcessManager &process_manager)
Definition Messenger.cxx:48
std::pair< Poller, std::size_t > create_queue_poller()
Helper function that creates a poller for Queue::loop()
void update_worker_channel_on_master(Channel &channel, bool more)
void debug_print(std::string s)
Function called from send and receive template functions in debug builds used to monitor the messages...
void send_from_queue_to_worker(std::size_t this_worker_id)
void test_send(X2X ping_value, test_snd_pipes snd_pipe, std::size_t worker_id)
Definition Messenger.cxx:79
Waits for input on a set of registered Channels.
Definition Poller.h:34
std::vector< std::size_t > poll(int timeout_ms=-1) const
Wait for input; returns the registration indices of readable channels.
Definition Poller.h:58
std::size_t register_channel(const Channel &channel)
Register a channel for input polling; returns its stable index.
Definition Poller.h:37
Fork processes for queue and workers.
int claim_mw_fd(std::size_t worker_ix)
Hand over the master-worker channel end for the current process type.
int claim_mq_fd()
Hand over the master-queue channel end for the current process type.
int claim_qw_fd(std::size_t worker_ix)
Hand over the queue-worker channel end for the current process type.
Thrown when a blocking wait on a Channel is interrupted, e.g.
Definition Channel.h:31
std::ostream & operator<<(std::ostream &out, const Message &msg)
Definition Message.h:72
std::tuple< std::vector< std::size_t >, bool > careful_poll(Poller &poller)
Definition util.cxx:66
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:73