PageRenderTime 32ms CodeModel.GetById 10ms app.highlight 17ms RepoModel.GetById 2ms app.codeStats 0ms

/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
  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
 19class CJobManager;
 20
 21class CJobWorker : public CThread
 22{
 23public:
 24  explicit CJobWorker(CJobManager *manager);
 25  ~CJobWorker() override;
 26
 27  void Process() override;
 28private:
 29  CJobManager  *m_jobManager;
 30};
 31
 32template<typename F>
 33class CLambdaJob : public CJob
 34{
 35public:
 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  };
 46private:
 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 */
 63class 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  };
 88public:
 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
160protected:
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
167private:
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 */
192class 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
227public:
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
312protected:
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
344private:
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};