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>();
51 auto state_id = JobManager::instance()->messenger().receive_from_master_on_queue<State>();
52 auto task_id = JobManager::instance()->messenger().receive_from_master_on_queue<Task>();
53 JobTask job_task{job_object_id, state_id, task_id};
55 N_tasks_++;
56 break;
57 }
58 }
59}
60
61/// Helper function for 'Queue::loop()'
63{
64 switch (message) {
65 case W2Q::dequeue: {
66 // dequeue 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)
71 JobManager::instance()->messenger().send_from_queue_to_worker(
74 } else {
75 JobManager::instance()->messenger().send_from_queue_to_worker(this_worker_id, Q2W::dequeue_rejected);
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());
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) {
102 auto message = JobManager::instance()->messenger().receive_from_master_on_queue<M2Q>();
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
109 auto message = JobManager::instance()->messenger().receive_from_worker_on_queue<W2Q>(this_worker_id);
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
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
static JobManager * instance()
Waits for input on a set of registered Channels.
Definition Poller.h:34
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