1 | #ifndef theplu_yat_utility_scheduler |
---|
2 | #define theplu_yat_utility_scheduler |
---|
3 | |
---|
4 | // $Id: Scheduler.h 3346 2014-11-06 13:16:34Z peter $ |
---|
5 | |
---|
6 | /* |
---|
7 | Copyright (C) 2014 Peter Johansson |
---|
8 | |
---|
9 | This file is part of the yat library, http://dev.thep.lu.se/yat |
---|
10 | |
---|
11 | The yat library is free software; you can redistribute it and/or |
---|
12 | modify it under the terms of the GNU General Public License as |
---|
13 | published by the Free Software Foundation; either version 3 of the |
---|
14 | License, or (at your option) any later version. |
---|
15 | |
---|
16 | The yat library is distributed in the hope that it will be useful, |
---|
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
19 | General Public License for more details. |
---|
20 | |
---|
21 | You should have received a copy of the GNU General Public License |
---|
22 | along with yat. If not, see <http://www.gnu.org/licenses/>. |
---|
23 | */ |
---|
24 | |
---|
25 | #include "Queue.h" |
---|
26 | |
---|
27 | #include <boost/thread.hpp> |
---|
28 | #include <boost/shared_ptr.hpp> |
---|
29 | |
---|
30 | #include <set> |
---|
31 | #include <deque> |
---|
32 | |
---|
33 | namespace theplu { |
---|
34 | namespace yat { |
---|
35 | namespace utility { |
---|
36 | |
---|
37 | /** |
---|
38 | \brief Handle a number of jobs and send them to threads |
---|
39 | |
---|
40 | Scheduler starts a (user defined) number of threads and handles a |
---|
41 | series of Jobs. The Jobs can have dependencies, such that Job X |
---|
42 | must finish before Job Y can run, and the Scheduler takes care of |
---|
43 | these dependencies. It possible to create new Jobs within a Job |
---|
44 | (see Job::submit) and these will be processed when the current |
---|
45 | Job has completed. Here is a small code example in which two |
---|
46 | threads are used to process the four jobs, \c MyJob. The jobs |
---|
47 | have a dependency that job4 can not run until the three first |
---|
48 | jobs have completed. |
---|
49 | |
---|
50 | \code |
---|
51 | |
---|
52 | Scheduler scheduler(2); |
---|
53 | boost::shared_ptr<MyJob> job1(new MyJob("Hello")); |
---|
54 | boost::shared_ptr<MyJob> job1(new MyJob(" ")); |
---|
55 | boost::shared_ptr<MyJob> job3(new MyJob("World")); |
---|
56 | boost::shared_ptr<MyJob> job4(new MyJob("\n")); |
---|
57 | job4.add_dependency(job1); |
---|
58 | job4.add_dependency(job2); |
---|
59 | job4.add_dependency(job3); |
---|
60 | scheduler.submit(job4); |
---|
61 | scheduler.launch(); |
---|
62 | |
---|
63 | \endcode |
---|
64 | */ |
---|
65 | class Scheduler |
---|
66 | { |
---|
67 | public: |
---|
68 | /** |
---|
69 | Base class that defines the interface for a Job |
---|
70 | */ |
---|
71 | class Job |
---|
72 | { |
---|
73 | public: |
---|
74 | /** |
---|
75 | \brief constructor |
---|
76 | */ |
---|
77 | Job(void); |
---|
78 | |
---|
79 | /** |
---|
80 | \brief destructor |
---|
81 | */ |
---|
82 | virtual ~Job(void); |
---|
83 | |
---|
84 | /** |
---|
85 | \brief add a dependency |
---|
86 | |
---|
87 | This job will not be processed until \a job has finished. |
---|
88 | */ |
---|
89 | void add_dependency(boost::shared_ptr<Job> job); |
---|
90 | |
---|
91 | /** |
---|
92 | This function defines the work done in thread. |
---|
93 | */ |
---|
94 | virtual void operator()(void)=0; |
---|
95 | protected: |
---|
96 | /** |
---|
97 | \brief Add a Job |
---|
98 | |
---|
99 | Typically called within operator(). When this Job has |
---|
100 | completed, the Scheduler will collect jobs passed to submit |
---|
101 | and send them to the queue just like if they had been passed |
---|
102 | to Scheduler::submit. |
---|
103 | */ |
---|
104 | void submit(boost::shared_ptr<Job> job); |
---|
105 | private: |
---|
106 | friend class Scheduler; |
---|
107 | // set of jobs that have to finish before this can run |
---|
108 | std::set<boost::shared_ptr<Job> > parents_; |
---|
109 | // jobs created within this job |
---|
110 | std::vector<boost::shared_ptr<Job> > sub_jobs_; |
---|
111 | enum status { pristine, prepared, running, completed}; |
---|
112 | status status_; |
---|
113 | }; // end class Job |
---|
114 | |
---|
115 | /** |
---|
116 | \brief constructor |
---|
117 | |
---|
118 | \param threads number of threads that are used |
---|
119 | */ |
---|
120 | Scheduler(unsigned int threads); |
---|
121 | |
---|
122 | /** |
---|
123 | Launch submitted jobs to threads and wait until all jobs have finished. |
---|
124 | */ |
---|
125 | void launch(void); |
---|
126 | |
---|
127 | /** |
---|
128 | Add \a job to list of jobs that should be processed in launch(void) |
---|
129 | */ |
---|
130 | void submit(boost::shared_ptr<Job> job); |
---|
131 | |
---|
132 | private: |
---|
133 | // \internal class that does the job |
---|
134 | // |
---|
135 | // It processes any job that is pushed to the \a queue until a |
---|
136 | // NULL Job shows up which signals that the work is done. When |
---|
137 | // NULL is observed the NULL Job is pushed to the queue so |
---|
138 | // co-workers are notified too. |
---|
139 | class Worker |
---|
140 | { |
---|
141 | public: |
---|
142 | Worker(Queue<boost::shared_ptr<Job> >& queue, |
---|
143 | Queue<boost::shared_ptr<Job> >& completed); |
---|
144 | void operator()(void); |
---|
145 | private: |
---|
146 | Queue<boost::shared_ptr<Job> >& queue_; |
---|
147 | Queue<boost::shared_ptr<Job> >& completed_; |
---|
148 | }; // end class Worker |
---|
149 | |
---|
150 | // function called when job has finished and returned from |
---|
151 | // worker. If there are any jobs that depends on \a job those jobs |
---|
152 | // are notified and if it makes them ready to be processed they |
---|
153 | // are sent to queue. |
---|
154 | void post_process(boost::shared_ptr<Job> job); |
---|
155 | |
---|
156 | // If \a job has parent jobs, which need to finish first, update |
---|
157 | // map children_ to reflect that. If all parents have finished, |
---|
158 | // send job to queue. |
---|
159 | void process(boost::shared_ptr<Job> job); |
---|
160 | |
---|
161 | // send job to queue |
---|
162 | void queue(boost::shared_ptr<Job> job); |
---|
163 | |
---|
164 | typedef boost::shared_ptr<Scheduler::Job> JobPtr; |
---|
165 | std::deque<JobPtr> jobs_; |
---|
166 | std::set<JobPtr> running_jobs_; |
---|
167 | |
---|
168 | // value lists all jobs that depend on key, i.e., key has to |
---|
169 | // finish before jobs in value can start |
---|
170 | std::map<JobPtr, std::vector<JobPtr> > children_; |
---|
171 | |
---|
172 | Queue<boost::shared_ptr<Job> > queue_; |
---|
173 | Queue<boost::shared_ptr<Job> > completed_; |
---|
174 | boost::thread_group workers_; |
---|
175 | |
---|
176 | }; // end class Scheduler |
---|
177 | |
---|
178 | }}} |
---|
179 | #endif |
---|