diff options
Diffstat (limited to 'xbmc/utils/JobManager.h')
| -rw-r--r-- | xbmc/utils/JobManager.h | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/xbmc/utils/JobManager.h b/xbmc/utils/JobManager.h new file mode 100644 index 0000000..ac4aa4e --- /dev/null +++ b/xbmc/utils/JobManager.h | |||
| @@ -0,0 +1,373 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2018 Team Kodi | ||
| 3 | * This file is part of Kodi - https://kodi.tv | ||
| 4 | * | ||
| 5 | * SPDX-License-Identifier: GPL-2.0-or-later | ||
| 6 | * See LICENSES/README.md for more information. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include "Job.h" | ||
| 12 | #include "threads/CriticalSection.h" | ||
| 13 | #include "threads/Thread.h" | ||
| 14 | |||
| 15 | #include <queue> | ||
| 16 | #include <string> | ||
| 17 | #include <vector> | ||
| 18 | |||
| 19 | class CJobManager; | ||
| 20 | |||
| 21 | class CJobWorker : public CThread | ||
| 22 | { | ||
| 23 | public: | ||
| 24 | explicit CJobWorker(CJobManager *manager); | ||
| 25 | ~CJobWorker() override; | ||
| 26 | |||
| 27 | void Process() override; | ||
| 28 | private: | ||
| 29 | CJobManager *m_jobManager; | ||
| 30 | }; | ||
| 31 | |||
| 32 | template<typename F> | ||
| 33 | class CLambdaJob : public CJob | ||
| 34 | { | ||
| 35 | public: | ||
| 36 | CLambdaJob(F&& f) : m_f(std::forward<F>(f)) {}; | ||
| 37 | bool DoWork() override | ||
| 38 | { | ||
| 39 | m_f(); | ||
| 40 | return true; | ||
| 41 | } | ||
| 42 | bool operator==(const CJob *job) const override | ||
| 43 | { | ||
| 44 | return this == job; | ||
| 45 | }; | ||
| 46 | private: | ||
| 47 | F m_f; | ||
| 48 | }; | ||
| 49 | |||
| 50 | /*! | ||
| 51 | \ingroup jobs | ||
| 52 | \brief Job Queue class to handle a queue of unique jobs to be processed sequentially | ||
| 53 | |||
| 54 | Holds a queue of jobs to be processed sequentially, either first in,first out | ||
| 55 | or last in, first out. Jobs are unique, so queueing multiple copies of the same job | ||
| 56 | (based on the CJob::operator==) will not add additional jobs. | ||
| 57 | |||
| 58 | Classes should subclass this class and override OnJobCallback should they require | ||
| 59 | information from the job. | ||
| 60 | |||
| 61 | \sa CJob and IJobCallback | ||
| 62 | */ | ||
| 63 | class CJobQueue: public IJobCallback | ||
| 64 | { | ||
| 65 | class CJobPointer | ||
| 66 | { | ||
| 67 | public: | ||
| 68 | explicit CJobPointer(CJob *job) | ||
| 69 | { | ||
| 70 | m_job = job; | ||
| 71 | m_id = 0; | ||
| 72 | }; | ||
| 73 | void CancelJob(); | ||
| 74 | void FreeJob() | ||
| 75 | { | ||
| 76 | delete m_job; | ||
| 77 | m_job = NULL; | ||
| 78 | }; | ||
| 79 | bool operator==(const CJob *job) const | ||
| 80 | { | ||
| 81 | if (m_job) | ||
| 82 | return *m_job == job; | ||
| 83 | return false; | ||
| 84 | }; | ||
| 85 | CJob *m_job; | ||
| 86 | unsigned int m_id; | ||
| 87 | }; | ||
| 88 | public: | ||
| 89 | /*! | ||
| 90 | \brief CJobQueue constructor | ||
| 91 | \param lifo whether the queue should be processed last in first out or first in first out. Defaults to false (first in first out) | ||
| 92 | \param jobsAtOnce number of jobs at once to process. Defaults to 1. | ||
| 93 | \param priority priority of this queue. | ||
| 94 | \sa CJob | ||
| 95 | */ | ||
| 96 | CJobQueue(bool lifo = false, unsigned int jobsAtOnce = 1, CJob::PRIORITY priority = CJob::PRIORITY_LOW); | ||
| 97 | |||
| 98 | /*! | ||
| 99 | \brief CJobQueue destructor | ||
| 100 | Cancels any in-process jobs, and destroys the job queue. | ||
| 101 | \sa CJob | ||
| 102 | */ | ||
| 103 | ~CJobQueue() override; | ||
| 104 | |||
| 105 | /*! | ||
| 106 | \brief Add a job to the queue | ||
| 107 | On completion of the job (or destruction of the job queue) the CJob object will be destroyed. | ||
| 108 | \param job a pointer to the job to add. The job should be subclassed from CJob. | ||
| 109 | \sa CJob | ||
| 110 | */ | ||
| 111 | bool AddJob(CJob *job); | ||
| 112 | |||
| 113 | /*! | ||
| 114 | \brief Add a function f to this job queue | ||
| 115 | */ | ||
| 116 | template<typename F> | ||
| 117 | void Submit(F&& f) | ||
| 118 | { | ||
| 119 | AddJob(new CLambdaJob<F>(std::forward<F>(f))); | ||
| 120 | } | ||
| 121 | |||
| 122 | /*! | ||
| 123 | \brief Cancel a job in the queue | ||
| 124 | Cancels a job in the queue. Any job currently being processed may complete after this | ||
| 125 | call has completed, but OnJobComplete will not be performed. If the job is only queued | ||
| 126 | then it will be removed from the queue and deleted. | ||
| 127 | \param job a pointer to the job to cancel. The job should be subclassed from CJob. | ||
| 128 | \sa CJob | ||
| 129 | */ | ||
| 130 | void CancelJob(const CJob *job); | ||
| 131 | |||
| 132 | /*! | ||
| 133 | \brief Cancel all jobs in the queue | ||
| 134 | Removes all jobs from the queue. Any job currently being processed may complete after this | ||
| 135 | call has completed, but OnJobComplete will not be performed. | ||
| 136 | \sa CJob | ||
| 137 | */ | ||
| 138 | void CancelJobs(); | ||
| 139 | |||
| 140 | /*! | ||
| 141 | \brief Check whether the queue is processing a job | ||
| 142 | */ | ||
| 143 | bool IsProcessing() const; | ||
| 144 | |||
| 145 | /*! | ||
| 146 | \brief The callback used when a job completes. | ||
| 147 | |||
| 148 | OnJobComplete is called at the completion of the CJob::DoWork function, and is used | ||
| 149 | to return information to the caller on the result of the job. On returning from this function | ||
| 150 | the CJobManager will destroy this job. | ||
| 151 | |||
| 152 | Subclasses should override this function if they wish to transfer information from the job prior | ||
| 153 | to it's deletion. They must then call this base class function, which will move on to the next | ||
| 154 | job. | ||
| 155 | |||
| 156 | \sa CJobManager, IJobCallback and CJob | ||
| 157 | */ | ||
| 158 | void OnJobComplete(unsigned int jobID, bool success, CJob *job) override; | ||
| 159 | |||
| 160 | protected: | ||
| 161 | /*! | ||
| 162 | \brief Returns if we still have jobs waiting to be processed | ||
| 163 | NOTE: This function does not take into account the jobs that are currently processing | ||
| 164 | */ | ||
| 165 | bool QueueEmpty() const; | ||
| 166 | |||
| 167 | private: | ||
| 168 | void QueueNextJob(); | ||
| 169 | |||
| 170 | typedef std::deque<CJobPointer> Queue; | ||
| 171 | typedef std::vector<CJobPointer> Processing; | ||
| 172 | Queue m_jobQueue; | ||
| 173 | Processing m_processing; | ||
| 174 | |||
| 175 | unsigned int m_jobsAtOnce; | ||
| 176 | CJob::PRIORITY m_priority; | ||
| 177 | mutable CCriticalSection m_section; | ||
| 178 | bool m_lifo; | ||
| 179 | }; | ||
| 180 | |||
| 181 | /*! | ||
| 182 | \ingroup jobs | ||
| 183 | \brief Job Manager class for scheduling asynchronous jobs. | ||
| 184 | |||
| 185 | Controls asynchronous job execution, by allowing clients to add and cancel jobs. | ||
| 186 | Should be accessed via CJobManager::GetInstance(). Jobs are allocated based on | ||
| 187 | priority levels. Lower priority jobs are executed only if there are sufficient | ||
| 188 | spare worker threads free to allow for higher priority jobs that may arise. | ||
| 189 | |||
| 190 | \sa CJob and IJobCallback | ||
| 191 | */ | ||
| 192 | class CJobManager final | ||
| 193 | { | ||
| 194 | class CWorkItem | ||
| 195 | { | ||
| 196 | public: | ||
| 197 | CWorkItem(CJob *job, unsigned int id, CJob::PRIORITY priority, IJobCallback *callback) | ||
| 198 | { | ||
| 199 | m_job = job; | ||
| 200 | m_id = id; | ||
| 201 | m_callback = callback; | ||
| 202 | m_priority = priority; | ||
| 203 | } | ||
| 204 | bool operator==(unsigned int jobID) const | ||
| 205 | { | ||
| 206 | return m_id == jobID; | ||
| 207 | }; | ||
| 208 | bool operator==(const CJob *job) const | ||
| 209 | { | ||
| 210 | return m_job == job; | ||
| 211 | }; | ||
| 212 | void FreeJob() | ||
| 213 | { | ||
| 214 | delete m_job; | ||
| 215 | m_job = NULL; | ||
| 216 | }; | ||
| 217 | void Cancel() | ||
| 218 | { | ||
| 219 | m_callback = NULL; | ||
| 220 | }; | ||
| 221 | CJob *m_job; | ||
| 222 | unsigned int m_id; | ||
| 223 | IJobCallback *m_callback; | ||
| 224 | CJob::PRIORITY m_priority; | ||
| 225 | }; | ||
| 226 | |||
| 227 | public: | ||
| 228 | /*! | ||
| 229 | \brief The only way through which the global instance of the CJobManager should be accessed. | ||
| 230 | \return the global instance. | ||
| 231 | */ | ||
| 232 | static CJobManager &GetInstance(); | ||
| 233 | |||
| 234 | /*! | ||
| 235 | \brief Add a job to the threaded job manager. | ||
| 236 | \param job a pointer to the job to add. The job should be subclassed from CJob | ||
| 237 | \param callback a pointer to an IJobCallback instance to receive job progress and completion notices. | ||
| 238 | \param priority the priority that this job should run at. | ||
| 239 | \return a unique identifier for this job, to be used with other interaction | ||
| 240 | \sa CJob, IJobCallback, CancelJob() | ||
| 241 | */ | ||
| 242 | unsigned int AddJob(CJob *job, IJobCallback *callback, CJob::PRIORITY priority = CJob::PRIORITY_LOW); | ||
| 243 | |||
| 244 | /*! | ||
| 245 | \brief Add a function f to this job manager for asynchronously execution. | ||
| 246 | */ | ||
| 247 | template<typename F> | ||
| 248 | void Submit(F&& f, CJob::PRIORITY priority = CJob::PRIORITY_LOW) | ||
| 249 | { | ||
| 250 | AddJob(new CLambdaJob<F>(std::forward<F>(f)), nullptr, priority); | ||
| 251 | } | ||
| 252 | |||
| 253 | /*! | ||
| 254 | \brief Add a function f to this job manager for asynchronously execution. | ||
| 255 | */ | ||
| 256 | template<typename F> | ||
| 257 | void Submit(F&& f, IJobCallback *callback, CJob::PRIORITY priority = CJob::PRIORITY_LOW) | ||
| 258 | { | ||
| 259 | AddJob(new CLambdaJob<F>(std::forward<F>(f)), callback, priority); | ||
| 260 | } | ||
| 261 | |||
| 262 | /*! | ||
| 263 | \brief Cancel a job with the given id. | ||
| 264 | \param jobID the id of the job to cancel, retrieved previously from AddJob() | ||
| 265 | \sa AddJob() | ||
| 266 | */ | ||
| 267 | void CancelJob(unsigned int jobID); | ||
| 268 | |||
| 269 | /*! | ||
| 270 | \brief Cancel all remaining jobs, preparing for shutdown | ||
| 271 | Should be called prior to destroying any objects that may be being used as callbacks | ||
| 272 | \sa CancelJob(), AddJob() | ||
| 273 | */ | ||
| 274 | void CancelJobs(); | ||
| 275 | |||
| 276 | /*! | ||
| 277 | \brief Re-start accepting jobs again | ||
| 278 | Called after calling CancelJobs() to allow this manager to accept more jobs | ||
| 279 | \throws std::logic_error if the manager was not previously cancelled | ||
| 280 | \sa CancelJobs() | ||
| 281 | */ | ||
| 282 | void Restart(); | ||
| 283 | |||
| 284 | /*! | ||
| 285 | \brief Checks to see if any jobs of a specific type are currently processing. | ||
| 286 | \param type Job type to search for | ||
| 287 | \return Number of matching jobs | ||
| 288 | */ | ||
| 289 | int IsProcessing(const std::string &type) const; | ||
| 290 | |||
| 291 | /*! | ||
| 292 | \brief Suspends queueing of jobs with priority PRIORITY_LOW_PAUSABLE until unpaused | ||
| 293 | Useful to (for ex) stop queuing thumb jobs during video start/playback. | ||
| 294 | Does not affect currently processing jobs, use IsProcessing to see if any need to be waited on | ||
| 295 | \sa UnPauseJobs() | ||
| 296 | */ | ||
| 297 | void PauseJobs(); | ||
| 298 | |||
| 299 | /*! | ||
| 300 | \brief Resumes queueing of (previously paused) jobs with priority PRIORITY_LOW_PAUSABLE | ||
| 301 | \sa PauseJobs() | ||
| 302 | */ | ||
| 303 | void UnPauseJobs(); | ||
| 304 | |||
| 305 | /*! | ||
| 306 | \brief Checks to see if any jobs with specific priority are currently processing. | ||
| 307 | \param priority to search for | ||
| 308 | \return true if processing jobs, else returns false | ||
| 309 | */ | ||
| 310 | bool IsProcessing(const CJob::PRIORITY &priority) const; | ||
| 311 | |||
| 312 | protected: | ||
| 313 | friend class CJobWorker; | ||
| 314 | friend class CJob; | ||
| 315 | friend class CJobQueue; | ||
| 316 | |||
| 317 | /*! | ||
| 318 | \brief Get a new job to process. Blocks until a new job is available, or a timeout has occurred. | ||
| 319 | \param worker a pointer to the current CJobWorker instance requesting a job. | ||
| 320 | \sa CJob | ||
| 321 | */ | ||
| 322 | CJob *GetNextJob(const CJobWorker *worker); | ||
| 323 | |||
| 324 | /*! | ||
| 325 | \brief Callback from CJobWorker after a job has completed. | ||
| 326 | Calls IJobCallback::OnJobComplete(), and then destroys job. | ||
| 327 | \param job a pointer to the calling subclassed CJob instance. | ||
| 328 | \param success the result from the DoWork call | ||
| 329 | \sa IJobCallback, CJob | ||
| 330 | */ | ||
| 331 | void OnJobComplete(bool success, CJob *job); | ||
| 332 | |||
| 333 | /*! | ||
| 334 | \brief Callback from CJob to report progress and check for cancellation. | ||
| 335 | Checks for cancellation, and calls IJobCallback::OnJobProgress(). | ||
| 336 | \param progress amount of processing performed to date, out of total. | ||
| 337 | \param total total amount of processing. | ||
| 338 | \param job pointer to the calling subclassed CJob instance. | ||
| 339 | \return true if the job has been cancelled, else returns false. | ||
| 340 | \sa IJobCallback, CJob | ||
| 341 | */ | ||
| 342 | bool OnJobProgress(unsigned int progress, unsigned int total, const CJob *job) const; | ||
| 343 | |||
| 344 | private: | ||
| 345 | // private construction, and no assignments; use the provided singleton methods | ||
| 346 | CJobManager(); | ||
| 347 | CJobManager(const CJobManager&) = delete; | ||
| 348 | CJobManager const& operator=(CJobManager const&) = delete; | ||
| 349 | |||
| 350 | /*! \brief Pop a job off the job queue and add to the processing queue ready to process | ||
| 351 | \return the job to process, NULL if no jobs are available | ||
| 352 | */ | ||
| 353 | CJob *PopJob(); | ||
| 354 | |||
| 355 | void StartWorkers(CJob::PRIORITY priority); | ||
| 356 | void RemoveWorker(const CJobWorker *worker); | ||
| 357 | static unsigned int GetMaxWorkers(CJob::PRIORITY priority); | ||
| 358 | |||
| 359 | unsigned int m_jobCounter; | ||
| 360 | |||
| 361 | typedef std::deque<CWorkItem> JobQueue; | ||
| 362 | typedef std::vector<CWorkItem> Processing; | ||
| 363 | typedef std::vector<CJobWorker*> Workers; | ||
| 364 | |||
| 365 | JobQueue m_jobQueue[CJob::PRIORITY_DEDICATED + 1]; | ||
| 366 | bool m_pauseJobs; | ||
| 367 | Processing m_processing; | ||
| 368 | Workers m_workers; | ||
| 369 | |||
| 370 | mutable CCriticalSection m_section; | ||
| 371 | CEvent m_jobEvent; | ||
| 372 | bool m_running; | ||
| 373 | }; | ||
