Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Queue.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
18
19#include <cassert>
20
21namespace RooFit {
22namespace MultiProcess {
23
24/** \class Queue
25 * \brief Keeps a queue of tasks for workers and manages the queue process through its event loop
26 *
27 * The Queue maintains a set of tasks on the queue process by receiving them
28 * from the master process. Worker processes can request to pop them off the
29 * queue. The communication between these processes is handled inside
30 * 'Queue::loop()', the queue process's event loop that polls the Messenger's
31 * sockets for incoming messages and handles them when they come.
32 *
33 * The reason for this class is to get automatic load balancing between
34 * workers. By allowing workers to request tasks whenever they are ready to
35 * do work, we don't need to manually distribute work over workers and they
36 * will always have something to do until all tasks have been completed.
37 * The alternative simple strategy of just distributing all tasks evenly over
38 * workers will be suboptimal when tasks have different or even varying
39 * runtimes (this simple strategy could be implemented with a PUSH-PULL
40 * ZeroMQ socket from master to workers, which would distribute tasks in a
41 * round-robin fashion, which, indeed, does not do load balancing).
42 */
43
44/// Helper function for 'Queue::loop()'
46{
47 switch (message) {
48 case M2Q::enqueue: {
49 // enqueue task
50 auto job_object_id = JobManager::instance()->messenger().receive_from_master_on_queue<std::size_t>();
53 JobTask job_task{job_object_id, state_id, task_id};
54 add(job_task);
55 N_tasks_++;
56 break;
57 }
58 }
59}
60
61/// Helper function for 'Queue::loop()'
62void Queue::process_worker_message(std::size_t this_worker_id, W2Q message)
63{
64 switch (message) {
65 case W2Q::dequeue: {
66 // dequeue task
67 JobTask job_task;
68 bool popped = pop(job_task);
69 if (popped) {
70 // Note: below two commands should be run atomically for thread safety (if that ever becomes an issue)
72 this_worker_id, Q2W::dequeue_accepted, job_task.job_id, job_task.state_id, job_task.task_id);
74 } else {
76 }
77 break;
78 }
79 }
80}
81
82/// \brief The queue process's event loop
83///
84/// Polls for incoming messages from other processes and handles them.
86{
87 assert(JobManager::instance()->process_manager().is_queue());
88 Poller poller;
89 std::size_t mq_index;
90 std::tie(poller, mq_index) = JobManager::instance()->messenger().create_queue_poller();
91
92 // The SIGTERM handler was set in the ProcessManager after forking to the queue and worker
93 // processes; it wakes up any poll through the self-pipe, so no signal blocking is needed here.
95 try { // watch for poll interruption caused by SIGTERM from master
96 // poll: wait until status change (-1: infinite timeout)
97 auto poll_result = poller.poll(-1);
98 // then process incoming messages from the channels
99 for (auto readable_index : poll_result) {
100 // message comes from the master/queue channel (first element):
101 if (readable_index == mq_index) {
103 process_master_message(message);
104 } else { // from a worker channel
105 // by construction of the queue poller: the master-queue channel is
106 // registered first (index 0), followed by the worker channels in
107 // worker-ID order
108 auto this_worker_id = readable_index - 1;
109 auto message = JobManager::instance()->messenger().receive_from_worker_on_queue<W2Q>(this_worker_id);
110 process_worker_message(this_worker_id, message);
111 }
112 }
113 } catch (ppoll_error_t &) {
114 // SIGTERM received (benign signal interruptions are retried inside
115 // Channel::wait), so exit the loop
116 break;
117 }
118 }
119}
120
121} // namespace MultiProcess
122} // namespace RooFit
static JobManager * instance()
std::pair< Poller, std::size_t > create_queue_poller()
Helper function that creates a poller for Queue::loop()
value_t receive_from_worker_on_queue(std::size_t this_worker_id)
Definition Messenger.h:45
void send_from_queue_to_worker(std::size_t this_worker_id)
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
virtual bool pop(JobTask &job_task)=0
Have a worker ask for a task-message from the queue.
void loop()
The queue process's event loop.
Definition Queue.cxx:85
virtual void add(JobTask job_task)=0
Enqueue a task.
void process_master_message(M2Q message)
Helper function for 'Queue::loop()'.
Definition Queue.cxx:45
std::size_t N_tasks_at_workers_
Definition Queue.h:44
void process_worker_message(std::size_t this_worker_id, W2Q message)
Helper function for 'Queue::loop()'.
Definition Queue.cxx:62
Thrown when a blocking wait on a Channel is interrupted, e.g.
Definition Channel.h:31
std::size_t Task
Definition types.h:22
std::size_t State
Definition types.h:23
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:73
combined job_object, state and task identifier type
Definition types.h:25