Stefan Scholz / ETL
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers scheduler.h Source File

scheduler.h

00001 /******************************************************************************
00002 The MIT License(MIT)
00003 
00004 Embedded Template Library.
00005 https://github.com/ETLCPP/etl
00006 http://www.etlcpp.com
00007 
00008 Copyright(c) 2017 jwellbelove
00009 
00010 Permission is hereby granted, free of charge, to any person obtaining a copy
00011 of this software and associated documentation files(the "Software"), to deal
00012 in the Software without restriction, including without limitation the rights
00013 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
00014 copies of the Software, and to permit persons to whom the Software is
00015 furnished to do so, subject to the following conditions :
00016 
00017 The above copyright notice and this permission notice shall be included in all
00018 copies or substantial portions of the Software.
00019 
00020 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00021 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00022 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
00023 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00024 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00025 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00026 SOFTWARE.
00027 ******************************************************************************/
00028 
00029 #ifndef __ETL_SCHEDULER__
00030 #define __ETL_SCHEDULER__
00031 
00032 #include <stdint.h>
00033 
00034 #include "platform.h "
00035 #include "vector.h "
00036 #include "nullptr.h "
00037 #include "error_handler.h "
00038 #include "exception.h "
00039 #include "task.h"
00040 #include "type_traits.h "
00041 #include "function.h "
00042 
00043 #undef ETL_FILE
00044 #define ETL_FILE "36"
00045 
00046 namespace etl
00047 {
00048   //***************************************************************************
00049   /// Base exception class for scheduler.
00050   //***************************************************************************
00051   class scheduler_exception : public etl::exception
00052   {
00053   public:
00054 
00055     scheduler_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
00056       : etl::exception(reason_, file_name_, line_number_)
00057     {
00058     }
00059   };
00060 
00061   //***************************************************************************
00062   /// 'No tasks' exception.
00063   //***************************************************************************
00064   class scheduler_no_tasks_exception : public etl::scheduler_exception
00065   {
00066   public:
00067 
00068     scheduler_no_tasks_exception(string_type file_name_, numeric_type line_number_)
00069       : etl::scheduler_exception(ETL_ERROR_TEXT("scheduler:no tasks", ETL_FILE"A"), file_name_, line_number_)
00070     {
00071     }
00072   };
00073 
00074   //***************************************************************************
00075   /// 'Null tasks' exception.
00076   //***************************************************************************
00077   class scheduler_null_task_exception : public etl::scheduler_exception
00078   {
00079   public:
00080 
00081     scheduler_null_task_exception(string_type file_name_, numeric_type line_number_)
00082       : etl::scheduler_exception(ETL_ERROR_TEXT("scheduler:null task", ETL_FILE"B"), file_name_, line_number_)
00083     {
00084     }
00085   };
00086 
00087   //***************************************************************************
00088   /// 'Too many tasks' exception.
00089   //***************************************************************************
00090   class scheduler_too_many_tasks_exception : public etl::scheduler_exception
00091   {
00092   public:
00093 
00094     scheduler_too_many_tasks_exception(string_type file_name_, numeric_type line_number_)
00095       : etl::scheduler_exception(ETL_ERROR_TEXT("scheduler:too many tasks", ETL_FILE"C"), file_name_, line_number_)
00096     {
00097     }
00098   };
00099 
00100   //***************************************************************************
00101   /// Sequencial Single.
00102   /// A policy the scheduler can use to decide what to do next.
00103   /// Only calls the task to process work once, if it has work to do.
00104   //***************************************************************************
00105   struct scheduler_policy_sequencial_single
00106   {
00107     bool schedule_tasks(etl::ivector<etl::task*> & task_list)
00108     {
00109       bool idle = true;
00110 
00111       for (size_t index = 0; index < task_list.size(); ++index)
00112       {
00113         etl::task& task = *(task_list[index]);
00114 
00115         if (task.task_request_work() > 0)
00116         {
00117           task.task_process_work();
00118           idle = false;
00119         }
00120       }
00121 
00122       return idle;
00123     }
00124   };
00125 
00126   //***************************************************************************
00127   /// Sequencial Multiple.
00128   /// A policy the scheduler can use to decide what to do next.
00129   /// Calls the task to process work until it reports that it has no more.
00130   //***************************************************************************
00131   struct scheduler_policy_sequencial_multiple
00132   {
00133     bool schedule_tasks(etl::ivector<etl::task*> & task_list)
00134     {
00135       bool idle = true;
00136 
00137       for (size_t index = 0; index < task_list.size(); ++index)
00138       {
00139         etl::task& task = *(task_list[index]);
00140 
00141         while (task.task_request_work() > 0)
00142         {
00143           task.task_process_work();
00144           idle = false;
00145         }
00146       }
00147 
00148       return idle;
00149     }
00150   };
00151 
00152   //***************************************************************************
00153   /// Highest Priority.
00154   /// A policy the scheduler can use to decide what to do next.
00155   /// Calls the highest priority task that has work.
00156   //***************************************************************************
00157   struct scheduler_policy_highest_priority
00158   {
00159     bool schedule_tasks(etl::ivector<etl::task*> & task_list)
00160     {
00161       bool idle = true;
00162 
00163       size_t index = 0;
00164       while (index < task_list.size())
00165       {
00166         etl::task& task = *(task_list[index]);
00167 
00168         if (task.task_request_work() > 0)
00169         {
00170           task.task_process_work();
00171           idle = false;
00172           break;
00173         }
00174         else
00175         {
00176           ++index;
00177         }
00178       }
00179 
00180       return idle;
00181     }
00182   };
00183 
00184   //***************************************************************************
00185   /// Most Work.
00186   /// A policy the scheduler can use to decide what to do next.
00187   /// Calls the task that has the most work.
00188   /// Starts looking from the task with the highest priority.
00189   //***************************************************************************
00190   struct scheduler_policy_most_work
00191   {
00192     bool schedule_tasks(etl::ivector<etl::task*> & task_list)
00193     {
00194       bool idle = true;
00195 
00196       size_t most_index = 0;
00197       uint_least8_t most_work = 0;
00198 
00199       for (size_t index = 0; index < task_list.size(); ++index)
00200       {
00201         etl::task& task = *(task_list[index]);
00202 
00203         uint_least8_t n_work = task.task_request_work();
00204 
00205         if (n_work > most_work)
00206         {
00207           most_index = index;
00208           most_work = n_work;
00209           idle = false;
00210         }
00211       }
00212 
00213       if (!idle)
00214       {
00215         task_list[most_index]->task_process_work();
00216       }
00217 
00218       return idle;
00219     }
00220   };
00221 
00222   //***************************************************************************
00223   /// Scheduler base.
00224   //***************************************************************************
00225   class ischeduler
00226   {
00227   public:
00228 
00229     //*******************************************
00230     // Virtuals.
00231     //*******************************************
00232     virtual void start() = 0;
00233 
00234     virtual ~ischeduler()
00235     {
00236     }
00237 
00238     //*******************************************
00239     /// Set the idle callback.
00240     //*******************************************
00241     void set_idle_callback(etl::ifunction<void>& callback)
00242     {
00243       p_idle_callback = &callback;
00244     }
00245 
00246     //*******************************************
00247     /// Set the watchdog callback.
00248     //*******************************************
00249     void set_watchdog_callback(etl::ifunction<void>& callback)
00250     {
00251       p_watchdog_callback = &callback;
00252     }
00253 
00254     //*******************************************
00255     /// Set the running state for the scheduler.
00256     //*******************************************
00257     void set_scheduler_running(bool scheduler_running_)
00258     {
00259       scheduler_running = scheduler_running_;
00260     }
00261 
00262     //*******************************************
00263     /// Get the running state for the scheduler.
00264     //*******************************************
00265     bool scheduler_is_running() const
00266     {
00267       return scheduler_running;
00268     }
00269 
00270     //*******************************************
00271     /// Force the scheduler to exit.
00272     //*******************************************
00273     void exit_scheduler()
00274     {
00275       scheduler_exit = true;
00276     }
00277 
00278     //*******************************************
00279     /// Add a task.
00280     /// Add to the task list in priority order.
00281     //*******************************************
00282     void add_task(etl::task& task)
00283     {
00284       ETL_ASSERT(!task_list.full(), ETL_ERROR(etl::scheduler_too_many_tasks_exception))
00285 
00286       if (!task_list.full())
00287       {
00288         typename task_list_t::iterator itask = std::upper_bound(task_list.begin(),
00289                                                                 task_list.end(),
00290                                                                 task.get_task_priority(),
00291                                                                 compare_priority());
00292 
00293         task_list.insert(itask, &task);
00294       }
00295     }
00296 
00297     //*******************************************
00298     /// Add a task list.
00299     /// Adds to the tasks to the internal task list in priority order.
00300     /// Input order is ignored.
00301     //*******************************************
00302     template <typename TSize>
00303     void add_task_list(etl::task** p_tasks, TSize size)
00304     {
00305       for (TSize i = 0; i < size; ++i)
00306       {
00307         ETL_ASSERT((p_tasks[i] != std::nullptr), ETL_ERROR(etl::scheduler_null_task_exception));
00308         add_task(*(p_tasks[i]));
00309       }
00310     }
00311 
00312   protected:
00313 
00314     //*******************************************
00315     /// Constructor.
00316     //*******************************************
00317     ischeduler(etl::ivector<etl::task*> & task_list_)
00318       : scheduler_running(false),
00319         scheduler_exit(false),
00320         p_idle_callback(std::nullptr),
00321         p_watchdog_callback(std::nullptr),
00322         task_list(task_list_)
00323     {
00324     }
00325 
00326     bool scheduler_running;
00327     bool scheduler_exit;
00328     etl::ifunction<void>* p_idle_callback;
00329     etl::ifunction<void>* p_watchdog_callback;
00330 
00331   private:
00332 
00333     //*******************************************
00334     // Used to order tasks in descending priority.
00335     //*******************************************
00336     struct compare_priority
00337     {
00338       bool operator()(etl::task* ptask, etl::task_priority_t priority) const
00339       {
00340         return ptask->get_task_priority() > priority;
00341       }
00342 
00343       bool operator()(etl::task_priority_t priority, etl::task* ptask) const
00344       {
00345         return priority > ptask->get_task_priority();
00346       }
00347     };
00348 
00349     typedef etl::ivector<etl::task*>  task_list_t;
00350     task_list_t& task_list;
00351   };
00352 
00353   //***************************************************************************
00354   /// Scheduler.
00355   //***************************************************************************
00356   template <typename TSchedulerPolicy, size_t MAX_TASKS_>
00357   class scheduler : public etl::ischeduler, protected TSchedulerPolicy
00358   {
00359   public:
00360 
00361     enum
00362     {
00363       MAX_TASKS = MAX_TASKS_,
00364     };
00365 
00366     scheduler()
00367       : ischeduler(task_list)
00368     {
00369     }
00370 
00371     //*******************************************
00372     /// Start the scheduler. SEQUENCIAL_SINGLE
00373     /// Only calls the task to process work once, if it has work to do.
00374     //*******************************************
00375     void start()
00376     {
00377       ETL_ASSERT(task_list.size() > 0, ETL_ERROR(etl::scheduler_no_tasks_exception));
00378 
00379       const size_t task_list_size = task_list.size();
00380 
00381       scheduler_running = true;
00382 
00383       while (!scheduler_exit)
00384       {
00385         if (scheduler_running)
00386         {
00387           bool idle = TSchedulerPolicy::schedule_tasks(task_list);
00388 
00389           if (p_watchdog_callback)
00390           {
00391             (*p_watchdog_callback)();
00392           }
00393 
00394           if (idle && p_idle_callback)
00395           {
00396             (*p_idle_callback)();
00397           }
00398         }
00399       }
00400     }
00401 
00402   private:
00403 
00404     typedef etl::vector<etl::task*, MAX_TASKS>  task_list_t;
00405     task_list_t task_list;
00406   };
00407 }
00408 
00409 #undef ETL_FILE
00410 
00411 #endif
00412