/xbmc/utils/JobManager.h

http://github.com/xbmc/xbmc · C Header · 373 lines · 166 code · 42 blank · 165 comment · 5 complexity · 868507f7602b2a6c436ec82107f76eb7 MD5 · raw file

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