[rtems commit] score: Introduce CPU budget operations

Sebastian Huber sebh at rtems.org
Mon Nov 15 08:15:16 UTC 2021


Module:    rtems
Branch:    master
Commit:    240a1f79b1fcb58b9ae7bfc393e1f890e4492683
Changeset: http://git.rtems.org/rtems/commit/?id=240a1f79b1fcb58b9ae7bfc393e1f890e4492683

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Wed Oct 27 12:59:09 2021 +0200

score: Introduce CPU budget operations

This patch set replaces the CPU budget algorithm enumeration with a set of CPU
budget operations which implement a particular CPU budget algorithm.  This
helps to hide the CPU budget algorithm implementation details from the general
thread handling.  The CPU budget callouts are turned into CPU budget
operations.  This slightly reduces the size of the thread control block.

All schedulers used the default scheduler tick implementation.  The tick
scheduler operation is removed and the CPU budget operations are directly used
in _Watchdog_Tick() if the executing thread uses a CPU budget algorithm.  This
is performance improvement for all threads which do not use a CPU budget
algorithm (default behaviour).

---

 cpukit/include/rtems/posix/pthreadimpl.h           |  15 +--
 cpukit/include/rtems/rtems/modesimpl.h             |   8 +-
 cpukit/include/rtems/score/scheduler.h             |  17 ----
 cpukit/include/rtems/score/schedulercbs.h          |  11 +--
 cpukit/include/rtems/score/scheduleredf.h          |   1 -
 cpukit/include/rtems/score/scheduleredfsmp.h       |   1 -
 cpukit/include/rtems/score/schedulerimpl.h         |  38 --------
 cpukit/include/rtems/score/schedulerpriority.h     |   1 -
 .../rtems/score/schedulerpriorityaffinitysmp.h     |   1 -
 cpukit/include/rtems/score/schedulerprioritysmp.h  |   1 -
 cpukit/include/rtems/score/schedulersimple.h       |   1 -
 cpukit/include/rtems/score/schedulersimplesmp.h    |   1 -
 cpukit/include/rtems/score/schedulerstrongapa.h    |   1 -
 cpukit/include/rtems/score/thread.h                |  94 ++++++++++---------
 cpukit/include/rtems/score/threadcpubudget.h       | 102 +++++++++++++++++++++
 cpukit/include/rtems/score/threadimpl.h            |  14 +--
 cpukit/posix/src/psxtransschedparam.c              |  44 ++++-----
 cpukit/posix/src/pthreadcreate.c                   |  36 +++++++-
 cpukit/posix/src/pthreadgetattrnp.c                |  16 ++--
 cpukit/posix/src/pthreadgetschedparam.c            |  14 +--
 cpukit/posix/src/pthreadsetschedparam.c            |  24 +++--
 cpukit/rtems/src/signalsend.c                      |  25 +++--
 cpukit/rtems/src/taskconstruct.c                   |   9 +-
 cpukit/rtems/src/taskmode.c                        |   2 +-
 cpukit/score/src/mpci.c                            |   1 -
 cpukit/score/src/schedulercbs.c                    |  41 ++++++++-
 cpukit/score/src/schedulercbsattachthread.c        |   5 +-
 cpukit/score/src/schedulercbsdetachthread.c        |  20 ++--
 cpukit/score/src/schedulercbsgetexecutiontime.c    |   2 +-
 cpukit/score/src/schedulercbsgetremainingbudget.c  |   2 +-
 cpukit/score/src/schedulercbsreleasejob.c          |   2 +-
 cpukit/score/src/schedulercbsunblock.c             |   2 +-
 cpukit/score/src/schedulerdefaulttick.c            |  81 ----------------
 cpukit/score/src/threadcreateidle.c                |   1 -
 cpukit/score/src/threaddispatch.c                  |  11 ++-
 cpukit/score/src/threadexhausttimeslice.c          |  53 +++++++++++
 cpukit/score/src/threadinitialize.c                |  12 +--
 cpukit/score/src/threadloadenv.c                   |  11 ++-
 cpukit/score/src/threadresettimeslice.c            |  91 ++++++++++++++++++
 cpukit/score/src/threadrestart.c                   |   4 -
 cpukit/score/src/watchdogtick.c                    |  25 +++--
 spec/build/cpukit/librtemscpu.yml                  |   4 +-
 testsuites/sptests/sptimecounter01/init.c          |   8 ++
 43 files changed, 520 insertions(+), 333 deletions(-)

diff --git a/cpukit/include/rtems/posix/pthreadimpl.h b/cpukit/include/rtems/posix/pthreadimpl.h
index 723b20e..0dc7d9d 100644
--- a/cpukit/include/rtems/posix/pthreadimpl.h
+++ b/cpukit/include/rtems/posix/pthreadimpl.h
@@ -47,7 +47,7 @@ RTEMS_INLINE_ROUTINE void _POSIX_Threads_Sporadic_timer_insert(
   POSIX_API_Control *api
 )
 {
-  the_thread->cpu_time_budget =
+  the_thread->CPU_budget.available =
     _Timespec_To_ticks( &api->Sporadic.sched_ss_init_budget );
 
   _Watchdog_Per_CPU_insert_ticks(
@@ -61,19 +61,12 @@ RTEMS_INLINE_ROUTINE void _POSIX_Threads_Sporadic_timer_insert(
 void _POSIX_Threads_Sporadic_timer( Watchdog_Control *watchdog );
 
 /**
- * @brief POSIX threads sporadic budget callout.
- *
- * This routine handles the sporadic scheduling algorithm.
- *
- * @param[in] the_thread is a pointer to the thread whose budget
- * has been exceeded.
+ * @brief The POSIX threads sporadic budget operations.
  */
-void _POSIX_Threads_Sporadic_budget_callout(
-  Thread_Control *the_thread
-);
+extern const Thread_CPU_budget_operations _POSIX_Threads_Sporadic_budget;
 
 int _POSIX_Thread_Translate_to_sched_policy(
-  Thread_CPU_budget_algorithms budget_algorithm
+  const Thread_CPU_budget_operations *operations
 );
 
 /**
diff --git a/cpukit/include/rtems/rtems/modesimpl.h b/cpukit/include/rtems/rtems/modesimpl.h
index 924e12f..8fdab26 100644
--- a/cpukit/include/rtems/rtems/modesimpl.h
+++ b/cpukit/include/rtems/rtems/modesimpl.h
@@ -22,6 +22,7 @@
 #include <rtems/score/schedulerimpl.h>
 #include <rtems/score/smpimpl.h>
 #include <rtems/score/threadimpl.h>
+#include <rtems/score/threadcpubudget.h>
 #include <rtems/config.h>
 
 #ifdef __cplusplus
@@ -148,10 +149,11 @@ RTEMS_INLINE_ROUTINE void _Modes_Apply_timeslice_to_thread(
 )
 {
   if ( _Modes_Is_timeslice( mode_set ) ) {
-    the_thread->budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE;
-    the_thread->cpu_time_budget = rtems_configuration_get_ticks_per_timeslice();
+    the_thread->CPU_budget.operations = &_Thread_CPU_budget_reset_timeslice;
+    the_thread->CPU_budget.available =
+      rtems_configuration_get_ticks_per_timeslice();
   } else {
-    the_thread->budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE;
+    the_thread->CPU_budget.operations = NULL;
   }
 }
 
diff --git a/cpukit/include/rtems/score/scheduler.h b/cpukit/include/rtems/score/scheduler.h
index df9477f..ad9d630 100644
--- a/cpukit/include/rtems/score/scheduler.h
+++ b/cpukit/include/rtems/score/scheduler.h
@@ -217,9 +217,6 @@ typedef struct {
     Thread_queue_Context *
   );
 
-  /** @see _Scheduler_Tick() */
-  void ( *tick )( const Scheduler_Control *, Thread_Control * );
-
   /** @see _Scheduler_Start_idle() */
   void ( *start_idle )(
     const Scheduler_Control *,
@@ -544,20 +541,6 @@ void _Scheduler_default_Cancel_job(
 );
 
 /**
- * @brief Performs tick operations depending on the CPU budget algorithm for
- * each executing thread.
- *
- * This routine is invoked as part of processing each clock tick.
- *
- * @param scheduler The scheduler.
- * @param[in, out] executing An executing thread.
- */
-void _Scheduler_default_Tick(
-  const Scheduler_Control *scheduler,
-  Thread_Control          *executing
-);
-
-/**
  * @brief Starts an idle thread.
  *
  * @param scheduler This parameter is unused.
diff --git a/cpukit/include/rtems/score/schedulercbs.h b/cpukit/include/rtems/score/schedulercbs.h
index 4b7efc8..8a9a49c 100644
--- a/cpukit/include/rtems/score/schedulercbs.h
+++ b/cpukit/include/rtems/score/schedulercbs.h
@@ -67,7 +67,6 @@ extern "C" {
     _Scheduler_default_Node_destroy, /* node destroy entry point */ \
     _Scheduler_CBS_Release_job,      /* new period of task */ \
     _Scheduler_CBS_Cancel_job,       /* cancel period of task */ \
-    _Scheduler_default_Tick,         /* tick entry point */ \
     _Scheduler_default_Start_idle    /* start idle entry point */ \
     SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \
   }
@@ -394,15 +393,9 @@ int _Scheduler_CBS_Set_parameters (
 );
 
 /**
- * @brief Invoked when a limited time quantum is exceeded.
- *
- * This routine is invoked when a limited time quantum is exceeded.
- *
- * @param the_thread The thread that exceeded a limited time quantum.
+ * @brief These are the CBS CPU budget operations.
  */
-void _Scheduler_CBS_Budget_callout(
-  Thread_Control *the_thread
-);
+extern const Thread_CPU_budget_operations _Scheduler_CBS_Budget;
 
 /**
  * @brief Initializes a CBS specific scheduler node of @a the_thread.
diff --git a/cpukit/include/rtems/score/scheduleredf.h b/cpukit/include/rtems/score/scheduleredf.h
index 9e643b9..2585632 100644
--- a/cpukit/include/rtems/score/scheduleredf.h
+++ b/cpukit/include/rtems/score/scheduleredf.h
@@ -66,7 +66,6 @@ extern "C" {
     _Scheduler_default_Node_destroy, /* node destroy entry point */ \
     _Scheduler_EDF_Release_job,      /* new period of task */ \
     _Scheduler_EDF_Cancel_job,       /* cancel period of task */ \
-    _Scheduler_default_Tick,         /* tick entry point */ \
     _Scheduler_default_Start_idle    /* start idle entry point */ \
     SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \
   }
diff --git a/cpukit/include/rtems/score/scheduleredfsmp.h b/cpukit/include/rtems/score/scheduleredfsmp.h
index 1841aa4..85e438e 100644
--- a/cpukit/include/rtems/score/scheduleredfsmp.h
+++ b/cpukit/include/rtems/score/scheduleredfsmp.h
@@ -128,7 +128,6 @@ typedef struct {
     _Scheduler_default_Node_destroy, \
     _Scheduler_EDF_Release_job, \
     _Scheduler_EDF_Cancel_job, \
-    _Scheduler_default_Tick, \
     _Scheduler_EDF_SMP_Start_idle, \
     _Scheduler_EDF_SMP_Set_affinity \
   }
diff --git a/cpukit/include/rtems/score/schedulerimpl.h b/cpukit/include/rtems/score/schedulerimpl.h
index 50110ea..2c53c05 100644
--- a/cpukit/include/rtems/score/schedulerimpl.h
+++ b/cpukit/include/rtems/score/schedulerimpl.h
@@ -600,44 +600,6 @@ RTEMS_INLINE_ROUTINE void _Scheduler_Cancel_job(
 }
 
 /**
- * @brief Scheduler method invoked at each clock tick.
- *
- * This method is invoked at each clock tick to allow the scheduler
- * implementation to perform any activities required.  For the
- * scheduler which support standard RTEMS features, this includes
- * time-slicing management.
- *
- * @param cpu The cpu control for the operation.
- */
-RTEMS_INLINE_ROUTINE void _Scheduler_Tick( const Per_CPU_Control *cpu )
-{
-  const Scheduler_Control *scheduler;
-  Thread_Control          *executing;
-
-  scheduler = _Scheduler_Get_by_CPU( cpu );
-
-#if defined(RTEMS_SMP)
-  if ( scheduler == NULL ) {
-    /*
-     * In SMP configurations, processors may be removed/added at runtime
-     * from/to a scheduler.  There may be still clock interrupts on currently
-     * unassigned processors.
-     */
-    return;
-  }
-#endif
-
-  /*
-   * Each online processor has at least an idle thread as the executing thread
-   * even in case it has currently no scheduler assigned.  Clock interrupts on
-   * processors which are not online would be a severe bug of the Clock Driver.
-   */
-  executing = _Per_CPU_Get_executing( cpu );
-  _Assert( executing != NULL );
-  ( *scheduler->Operations.tick )( scheduler, executing );
-}
-
-/**
  * @brief Starts the idle thread for a particular processor.
  *
  * @param scheduler The scheduler instance.
diff --git a/cpukit/include/rtems/score/schedulerpriority.h b/cpukit/include/rtems/score/schedulerpriority.h
index e0991f5..1ddbb4a 100644
--- a/cpukit/include/rtems/score/schedulerpriority.h
+++ b/cpukit/include/rtems/score/schedulerpriority.h
@@ -57,7 +57,6 @@ extern "C" {
     _Scheduler_default_Node_destroy,      /* node destroy entry point */ \
     _Scheduler_default_Release_job,       /* new period of task */ \
     _Scheduler_default_Cancel_job,        /* cancel period of task */ \
-    _Scheduler_default_Tick,              /* tick entry point */ \
     _Scheduler_default_Start_idle         /* start idle entry point */ \
     SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \
   }
diff --git a/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h b/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h
index 1b660fa..e997e81 100644
--- a/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h
+++ b/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h
@@ -73,7 +73,6 @@ extern "C" {
     _Scheduler_default_Node_destroy, \
     _Scheduler_default_Release_job, \
     _Scheduler_default_Cancel_job, \
-    _Scheduler_default_Tick, \
     _Scheduler_SMP_Start_idle, \
     _Scheduler_priority_affinity_SMP_Set_affinity \
   }
diff --git a/cpukit/include/rtems/score/schedulerprioritysmp.h b/cpukit/include/rtems/score/schedulerprioritysmp.h
index 56f4aa5..fe314fb 100644
--- a/cpukit/include/rtems/score/schedulerprioritysmp.h
+++ b/cpukit/include/rtems/score/schedulerprioritysmp.h
@@ -101,7 +101,6 @@ typedef struct {
     _Scheduler_default_Node_destroy, \
     _Scheduler_default_Release_job, \
     _Scheduler_default_Cancel_job, \
-    _Scheduler_default_Tick, \
     _Scheduler_SMP_Start_idle \
     SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \
   }
diff --git a/cpukit/include/rtems/score/schedulersimple.h b/cpukit/include/rtems/score/schedulersimple.h
index 63310e9..15471a6 100644
--- a/cpukit/include/rtems/score/schedulersimple.h
+++ b/cpukit/include/rtems/score/schedulersimple.h
@@ -56,7 +56,6 @@ extern "C" {
     _Scheduler_default_Node_destroy,      /* node destroy entry point */ \
     _Scheduler_default_Release_job,       /* new period of task */ \
     _Scheduler_default_Cancel_job,        /* cancel period of task */ \
-    _Scheduler_default_Tick,              /* tick entry point */ \
     _Scheduler_default_Start_idle         /* start idle entry point */ \
     SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \
   }
diff --git a/cpukit/include/rtems/score/schedulersimplesmp.h b/cpukit/include/rtems/score/schedulersimplesmp.h
index 961e60a..c839478 100644
--- a/cpukit/include/rtems/score/schedulersimplesmp.h
+++ b/cpukit/include/rtems/score/schedulersimplesmp.h
@@ -83,7 +83,6 @@ typedef struct {
     _Scheduler_default_Node_destroy, \
     _Scheduler_default_Release_job, \
     _Scheduler_default_Cancel_job, \
-    _Scheduler_default_Tick, \
     _Scheduler_SMP_Start_idle \
     SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \
   }
diff --git a/cpukit/include/rtems/score/schedulerstrongapa.h b/cpukit/include/rtems/score/schedulerstrongapa.h
index a3a19d8..bbded1b 100644
--- a/cpukit/include/rtems/score/schedulerstrongapa.h
+++ b/cpukit/include/rtems/score/schedulerstrongapa.h
@@ -169,7 +169,6 @@ typedef struct {
     _Scheduler_default_Node_destroy, \
     _Scheduler_default_Release_job, \
     _Scheduler_default_Cancel_job, \
-    _Scheduler_default_Tick, \
     _Scheduler_strong_APA_Start_idle, \
     _Scheduler_strong_APA_Set_affinity \
   }
diff --git a/cpukit/include/rtems/score/thread.h b/cpukit/include/rtems/score/thread.h
index aff2f58..4c8a97c 100644
--- a/cpukit/include/rtems/score/thread.h
+++ b/cpukit/include/rtems/score/thread.h
@@ -76,14 +76,6 @@ extern "C" {
  *@{
  */
 
-#define RTEMS_SCORE_THREAD_ENABLE_EXHAUST_TIMESLICE
-
-/*
- * With the addition of the Constant Block Scheduler (CBS),
- * this feature is needed even when POSIX is disabled.
- */
-#define RTEMS_SCORE_THREAD_ENABLE_SCHEDULER_CALLOUT
-
 #if defined(RTEMS_DEBUG)
 #define RTEMS_SCORE_THREAD_ENABLE_RESOURCE_COUNT
 #endif
@@ -148,27 +140,47 @@ typedef struct {
 } Thread_Entry_information;
 
 /**
- *  The following lists the algorithms used to manage the thread cpu budget.
- *
- *  Reset Timeslice:   At each context switch, reset the time quantum.
- *  Exhaust Timeslice: Only reset the quantum once it is consumed.
- *  Callout:           Execute routine when budget is consumed.
+ * @brief This structure contains operations which manage the CPU budget of a
+ *   thread.
  */
-typedef enum {
-  THREAD_CPU_BUDGET_ALGORITHM_NONE,
-  THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE,
-  #if defined(RTEMS_SCORE_THREAD_ENABLE_EXHAUST_TIMESLICE)
-    THREAD_CPU_BUDGET_ALGORITHM_EXHAUST_TIMESLICE,
-  #endif
-  #if defined(RTEMS_SCORE_THREAD_ENABLE_SCHEDULER_CALLOUT)
-    THREAD_CPU_BUDGET_ALGORITHM_CALLOUT
-  #endif
-}  Thread_CPU_budget_algorithms;
+typedef struct {
+  /**
+   * @brief This operation is called at each clock tick for the executing
+   *   thread.
+   */
+  void ( *at_tick )( Thread_Control * );
 
-/**  This defines thes the entry point for the thread specific timeslice
- *   budget management algorithm.
+  /**
+   * @brief This operation is called right before a context switch to the
+   *   thread is performed.
+   */
+  void ( *at_context_switch )( Thread_Control * );
+
+  /**
+   * @brief This operation is called to initialize the CPU budget of the
+   *   thread.
+   */
+  void ( *initialize )( Thread_Control * );
+} Thread_CPU_budget_operations;
+
+/**
+ * @brief This structure is used to control the CPU budget of a thread.
  */
-typedef void (*Thread_CPU_budget_algorithm_callout )( Thread_Control * );
+typedef struct {
+  /**
+   * @brief If this member is not NULL, then it references the CPU budget
+   *   operations used to manage the CPU budget of the thread, otherwise it is
+   *   NULL.
+   */
+  const Thread_CPU_budget_operations *operations;
+
+  /**
+   * @brief This member contains the count of the time quantum that this thread
+   *   is allowed to consume until an action takes place defined by the CPU
+   *   budget operations.
+   */
+  uint32_t available;
+} Thread_CPU_budget_control;
 
 /**
  *  The following structure contains the information which defines
@@ -182,12 +194,13 @@ typedef struct {
     * it started.
     */
   bool                                 is_preemptible;
-  /** This field indicates the CPU budget algorith. */
-  Thread_CPU_budget_algorithms         budget_algorithm;
-  /** This field is the routine to invoke when the CPU allotment is
-   *  consumed.
+
+  /**
+   * @brief This member may provide the CPU budget operations activated when a
+   *   thread is initialized before it is started or restarted.
    */
-  Thread_CPU_budget_algorithm_callout  budget_callout;
+  const Thread_CPU_budget_operations *cpu_budget_operations;
+
   /** This field is the initial ISR disable level of this thread. */
   uint32_t                             isr_level;
   /** This field is the initial priority. */
@@ -772,9 +785,7 @@ struct _Thread_Control {
    * the following fields
    *
    * - RTEMS_API_Control::Signal,
-   * - Thread_Control::budget_algorithm,
-   * - Thread_Control::budget_callout,
-   * - Thread_Control::cpu_time_budget,
+   * - Thread_Control::CPU_budget,
    * - Thread_Control::current_state,
    * - Thread_Control::Post_switch_actions,
    * - Thread_Control::Scheduler::control, and
@@ -841,18 +852,11 @@ struct _Thread_Control {
    */
   bool was_created_with_inherited_scheduler;
 
-  /** This field is the length of the time quantum that this thread is
-   *  allowed to consume.  The algorithm used to manage limits on CPU usage
-   *  is specified by budget_algorithm.
-   */
-  uint32_t                              cpu_time_budget;
-  /** This field is the algorithm used to manage this thread's time
-   *  quantum.  The algorithm may be specified as none which case,
-   *  no limit is in place.
+  /**
+   * @brief This member contains the CPU budget control used to manage the CPU
+   *   budget of the thread.
    */
-  Thread_CPU_budget_algorithms          budget_algorithm;
-  /** This field is the method invoked with the budgeted time is consumed. */
-  Thread_CPU_budget_algorithm_callout   budget_callout;
+  Thread_CPU_budget_control CPU_budget;
 
   /**
    * @brief This member contains the amount of CPU time consumed by this thread
diff --git a/cpukit/include/rtems/score/threadcpubudget.h b/cpukit/include/rtems/score/threadcpubudget.h
new file mode 100644
index 0000000..bcbaa11
--- /dev/null
+++ b/cpukit/include/rtems/score/threadcpubudget.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSScoreThread
+ *
+ * @brief This header file provides interfaces used to implement the CPU budget
+ *   management of threads.
+ */
+
+/*
+ * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTEMS_SCORE_THREADCPUBUDGET_H
+#define _RTEMS_SCORE_THREADCPUBUDGET_H
+
+#include <rtems/score/thread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup RTEMSScoreThread
+ *
+ * @{
+ */
+
+/**
+ * @brief Does nothing.
+ *
+ * @param the_thread is an unused parameter.
+ */
+void _Thread_CPU_budget_do_nothing( Thread_Control *the_thread );
+
+/**
+ * @brief Sets the available CPU budget of the thread to the configured clock
+ *   ticks per timeslice.
+ *
+ * @param the_thread is the thread to set the available CPU budget.
+ */
+void _Thread_CPU_budget_set_to_ticks_per_timeslice(
+  Thread_Control *the_thread
+);
+
+/**
+ * @brief Consumes one time quantum of the available CPU budget of the thread
+ *   and yields the thread if the available CPU budget is fully consumed.
+ *
+ * While the thread enabled the non-preemptive mode or is not ready, no time
+ * quantum is consumed.
+ *
+ * @param the_thread is the thread to operate on.
+ */
+void _Thread_CPU_budget_consume_and_yield( Thread_Control *the_thread );
+
+/**
+ * @brief These CPU budget operations allocate timeslices to the thread.
+ *
+ * The timeslice is not reset at a context switch to the thread.  Once a
+ * timeslice is consumed, the thread yields.
+ */
+extern const Thread_CPU_budget_operations _Thread_CPU_budget_exhaust_timeslice;
+
+/**
+ * @brief These CPU budget operations allocate timeslices to the thread.
+ *
+ * The timeslice is reset at a context switch to the thread.  Once a timeslice
+ * is consumed, the thread yields.
+ */
+extern const Thread_CPU_budget_operations _Thread_CPU_budget_reset_timeslice;
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTEMS_SCORE_THREADCPUBUDGET_H */
diff --git a/cpukit/include/rtems/score/threadimpl.h b/cpukit/include/rtems/score/threadimpl.h
index a983975..b1ec05f 100644
--- a/cpukit/include/rtems/score/threadimpl.h
+++ b/cpukit/include/rtems/score/threadimpl.h
@@ -176,19 +176,9 @@ typedef struct {
   Priority_Control priority;
 
   /**
-   * @brief The thread's budget algorithm.
+   * @brief The thread's initial CPU budget operations.
    */
-  Thread_CPU_budget_algorithms budget_algorithm;
-
-  /**
-   * @brief The thread's initial budget callout.
-   */
-  Thread_CPU_budget_algorithm_callout budget_callout;
-
-  /**
-   * @brief The thread's initial CPU time budget.
-   */
-  uint32_t cpu_time_budget;
+  const Thread_CPU_budget_operations *cpu_budget_operations;
 
   /**
    * @brief 32-bit unsigned integer name of the object for the thread.
diff --git a/cpukit/posix/src/psxtransschedparam.c b/cpukit/posix/src/psxtransschedparam.c
index eba26d4..f97924e 100644
--- a/cpukit/posix/src/psxtransschedparam.c
+++ b/cpukit/posix/src/psxtransschedparam.c
@@ -23,22 +23,27 @@
 #include <errno.h>
 
 #include <rtems/posix/pthreadimpl.h>
+#include <rtems/score/threadcpubudget.h>
 
 int _POSIX_Thread_Translate_to_sched_policy(
-  Thread_CPU_budget_algorithms budget_algorithm
+  const Thread_CPU_budget_operations *operations
 )
 {
-  switch ( budget_algorithm ) {
-    case THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE:
-      return SCHED_OTHER;
-    case THREAD_CPU_BUDGET_ALGORITHM_EXHAUST_TIMESLICE:
-      return SCHED_RR;
-    case THREAD_CPU_BUDGET_ALGORITHM_CALLOUT:
-      return SCHED_SPORADIC;
-    default:
-      _Assert( budget_algorithm == THREAD_CPU_BUDGET_ALGORITHM_NONE );
-      return SCHED_FIFO;
+  if ( operations == NULL ) {
+    return SCHED_FIFO;
   }
+
+  if ( operations == &_Thread_CPU_budget_exhaust_timeslice ) {
+    return SCHED_RR;
+  }
+
+#if defined(RTEMS_POSIX_API)
+  if ( operations == &_POSIX_Threads_Sporadic_budget ) {
+    return SCHED_SPORADIC;
+  }
+#endif
+
+  return SCHED_OTHER;
 }
 
 int _POSIX_Thread_Translate_sched_param(
@@ -47,23 +52,19 @@ int _POSIX_Thread_Translate_sched_param(
   Thread_Configuration     *config
 )
 {
-  config->budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE;
-  config->budget_callout = NULL;
-  config->cpu_time_budget = 0;
+  config->cpu_budget_operations = NULL;
 
-  if ( policy == SCHED_OTHER ) {
-    config->budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE;
+  if ( policy == SCHED_FIFO ) {
     return 0;
   }
 
-  if ( policy == SCHED_FIFO ) {
-    config->budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE;
+  if ( policy == SCHED_OTHER ) {
+    config->cpu_budget_operations = &_Thread_CPU_budget_reset_timeslice;
     return 0;
   }
 
   if ( policy == SCHED_RR ) {
-    config->budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_EXHAUST_TIMESLICE;
-    config->cpu_time_budget = rtems_configuration_get_ticks_per_timeslice();
+    config->cpu_budget_operations = &_Thread_CPU_budget_exhaust_timeslice;
     return 0;
   }
 
@@ -81,8 +82,7 @@ int _POSIX_Thread_Translate_sched_param(
 	 _Timespec_To_ticks( &param->sched_ss_init_budget ) )
       return EINVAL;
 
-    config->budget_algorithm  = THREAD_CPU_BUDGET_ALGORITHM_CALLOUT;
-    config->budget_callout = _POSIX_Threads_Sporadic_budget_callout;
+    config->cpu_budget_operations = &_POSIX_Threads_Sporadic_budget;
     return 0;
   }
 #endif
diff --git a/cpukit/posix/src/pthreadcreate.c b/cpukit/posix/src/pthreadcreate.c
index 9474d07..093ad5c 100644
--- a/cpukit/posix/src/pthreadcreate.c
+++ b/cpukit/posix/src/pthreadcreate.c
@@ -34,9 +34,11 @@
 #include <rtems/posix/pthreadimpl.h>
 #include <rtems/posix/pthreadattrimpl.h>
 #include <rtems/score/assert.h>
+#include <rtems/score/threadcpubudget.h>
 #include <rtems/score/threadimpl.h>
 #include <rtems/score/apimutex.h>
 #include <rtems/score/stackimpl.h>
+#include <rtems/score/statesimpl.h>
 #include <rtems/score/schedulerimpl.h>
 #include <rtems/score/userextimpl.h>
 #include <rtems/sysinit.h>
@@ -348,7 +350,9 @@ void _POSIX_Threads_Sporadic_timer( Watchdog_Control *watchdog )
   _Thread_Priority_update( &queue_context );
 }
 
-void _POSIX_Threads_Sporadic_budget_callout( Thread_Control *the_thread )
+static void _POSIX_Threads_Sporadic_budget_callout(
+  Thread_Control *the_thread
+)
 {
   POSIX_API_Control    *api;
   Thread_queue_Context  queue_context;
@@ -363,7 +367,7 @@ void _POSIX_Threads_Sporadic_budget_callout( Thread_Control *the_thread )
    *  This will prevent the thread from consuming its entire "budget"
    *  while at low priority.
    */
-  the_thread->cpu_time_budget = UINT32_MAX;
+  the_thread->CPU_budget.available = UINT32_MAX;
 
   if ( !_Priority_Node_is_active( &api->Sporadic.Low_priority ) ) {
     _Thread_Priority_add(
@@ -382,6 +386,34 @@ void _POSIX_Threads_Sporadic_budget_callout( Thread_Control *the_thread )
   _Thread_Priority_update( &queue_context );
 }
 
+static void _POSIX_Threads_Sporadic_budget_at_tick( Thread_Control *the_thread )
+{
+  uint32_t budget_available;
+
+  if ( !the_thread->is_preemptible ) {
+    return;
+  }
+
+  if ( !_States_Is_ready( the_thread->current_state ) ) {
+    return;
+  }
+
+  budget_available = the_thread->CPU_budget.available;
+
+  if ( budget_available == 1 ) {
+    the_thread->CPU_budget.available = 0;
+    _POSIX_Threads_Sporadic_budget_callout ( the_thread );
+  } else {
+    the_thread->CPU_budget.available = budget_available - 1;
+  }
+}
+
+const Thread_CPU_budget_operations _POSIX_Threads_Sporadic_budget = {
+  .at_tick = _POSIX_Threads_Sporadic_budget_at_tick,
+  .at_context_switch = _Thread_CPU_budget_do_nothing,
+  .initialize = _Thread_CPU_budget_do_nothing
+};
+
 static bool _POSIX_Threads_Create_extension(
   Thread_Control *executing,
   Thread_Control *created
diff --git a/cpukit/posix/src/pthreadgetattrnp.c b/cpukit/posix/src/pthreadgetattrnp.c
index 5572fb9..aa34185 100644
--- a/cpukit/posix/src/pthreadgetattrnp.c
+++ b/cpukit/posix/src/pthreadgetattrnp.c
@@ -37,12 +37,12 @@ int pthread_getattr_np(
   pthread_attr_t *attr
 )
 {
-  Thread_Control               *the_thread;
-  ISR_lock_Context              lock_context;
-  Thread_CPU_budget_algorithms  budget_algorithm;
-  const Scheduler_Control      *scheduler;
-  Priority_Control              priority;
-  Status_Control                status;
+  Thread_Control                     *the_thread;
+  ISR_lock_Context                    lock_context;
+  const Thread_CPU_budget_operations *cpu_budget_operations;
+  const Scheduler_Control            *scheduler;
+  Priority_Control                    priority;
+  Status_Control                      status;
 
   if ( attr == NULL ) {
     return EINVAL;
@@ -89,7 +89,7 @@ int pthread_getattr_np(
     attr->affinityset
   );
 
-  budget_algorithm = the_thread->budget_algorithm;
+  cpu_budget_operations = the_thread->CPU_budget.operations;
 
   _Thread_State_release( the_thread, &lock_context );
 
@@ -101,7 +101,7 @@ int pthread_getattr_np(
     priority
   );
   attr->schedpolicy =
-    _POSIX_Thread_Translate_to_sched_policy( budget_algorithm );
+    _POSIX_Thread_Translate_to_sched_policy( cpu_budget_operations );
 
   return _POSIX_Get_error( status );
 }
diff --git a/cpukit/posix/src/pthreadgetschedparam.c b/cpukit/posix/src/pthreadgetschedparam.c
index a82d79c..406ae8e 100644
--- a/cpukit/posix/src/pthreadgetschedparam.c
+++ b/cpukit/posix/src/pthreadgetschedparam.c
@@ -37,11 +37,11 @@ int pthread_getschedparam(
   struct sched_param *param
 )
 {
-  Thread_Control               *the_thread;
-  Thread_queue_Context          queue_context;
-  Thread_CPU_budget_algorithms  budget_algorithm;
-  const Scheduler_Control      *scheduler;
-  Priority_Control              priority;
+  Thread_Control                     *the_thread;
+  Thread_queue_Context                queue_context;
+  const Thread_CPU_budget_operations *cpu_budget_operations;
+  const Scheduler_Control            *scheduler;
+  Priority_Control                    priority;
 
   if ( policy == NULL || param == NULL ) {
     return EINVAL;
@@ -59,11 +59,11 @@ int pthread_getschedparam(
   scheduler = _Thread_Scheduler_get_home( the_thread );
   _POSIX_Threads_Get_sched_param_sporadic( the_thread, scheduler, param );
   priority = the_thread->Real_priority.priority;
-  budget_algorithm = the_thread->budget_algorithm;
+  cpu_budget_operations = the_thread->CPU_budget.operations;
 
   _Thread_Wait_release( the_thread, &queue_context );
 
   param->sched_priority = _POSIX_Priority_From_core( scheduler, priority );
-  *policy = _POSIX_Thread_Translate_to_sched_policy( budget_algorithm );
+  *policy = _POSIX_Thread_Translate_to_sched_policy( cpu_budget_operations );
   return 0;
 }
diff --git a/cpukit/posix/src/pthreadsetschedparam.c b/cpukit/posix/src/pthreadsetschedparam.c
index 1c207e7..165e1d8 100644
--- a/cpukit/posix/src/pthreadsetschedparam.c
+++ b/cpukit/posix/src/pthreadsetschedparam.c
@@ -40,14 +40,15 @@ static int _POSIX_Set_sched_param(
   Thread_queue_Context       *queue_context
 )
 {
-  const Scheduler_Control *scheduler;
-  int                      normal_prio;
-  bool                     valid;
-  Priority_Control         core_normal_prio;
+  const Scheduler_Control            *scheduler;
+  int                                 normal_prio;
+  bool                                valid;
+  Priority_Control                    core_normal_prio;
+  const Thread_CPU_budget_operations *cpu_budget_operations;
 #if defined(RTEMS_POSIX_API)
-  POSIX_API_Control       *api;
-  int                      low_prio;
-  Priority_Control         core_low_prio;
+  POSIX_API_Control                  *api;
+  int                                 low_prio;
+  Priority_Control                    core_low_prio;
 #endif
 
   normal_prio = param->sched_priority;
@@ -103,9 +104,12 @@ static int _POSIX_Set_sched_param(
   }
 #endif
 
-  the_thread->cpu_time_budget  = config->cpu_time_budget;
-  the_thread->budget_algorithm = config->budget_algorithm;
-  the_thread->budget_callout   = config->budget_callout;
+  cpu_budget_operations = config->cpu_budget_operations;
+  the_thread->CPU_budget.operations = cpu_budget_operations;
+
+  if ( cpu_budget_operations != NULL ) {
+    ( *cpu_budget_operations->initialize )( the_thread );
+  }
 
 #if defined(RTEMS_POSIX_API)
   _Priority_Node_set_priority( &api->Sporadic.Low_priority, core_low_prio );
diff --git a/cpukit/rtems/src/signalsend.c b/cpukit/rtems/src/signalsend.c
index 72407e2..6ce59f2 100644
--- a/cpukit/rtems/src/signalsend.c
+++ b/cpukit/rtems/src/signalsend.c
@@ -35,16 +35,15 @@ static void _Signal_Action_handler(
   ISR_lock_Context *lock_context
 )
 {
-  RTEMS_API_Control           *api;
-  ASR_Information             *asr;
-  rtems_signal_set             signal_set;
-  bool                         normal_is_preemptible;
-  uint32_t                     normal_cpu_time_budget;
-  Thread_CPU_budget_algorithms normal_budget_algorithm;
-  uint32_t                     normal_isr_level;
-  uint32_t                     before_call_isr_level;
-  bool                         after_call_is_preemptible;
-  bool                         after_call_asr_is_enabled;
+  RTEMS_API_Control        *api;
+  ASR_Information          *asr;
+  rtems_signal_set          signal_set;
+  bool                      normal_is_preemptible;
+  Thread_CPU_budget_control normal_cpu_budget;
+  uint32_t                  normal_isr_level;
+  uint32_t                  before_call_isr_level;
+  bool                      after_call_is_preemptible;
+  bool                      after_call_asr_is_enabled;
 
   (void) action;
 
@@ -69,8 +68,7 @@ static void _Signal_Action_handler(
 
   _Assert( asr->is_enabled );
   normal_is_preemptible = executing->is_preemptible;
-  normal_cpu_time_budget = executing->cpu_time_budget;
-  normal_budget_algorithm = executing->budget_algorithm;
+  normal_cpu_budget = executing->CPU_budget;
 
   /* Set mode for ASR processing */
 
@@ -102,8 +100,7 @@ static void _Signal_Action_handler(
 
   _Thread_State_acquire( executing, lock_context );
 
-  executing->cpu_time_budget = normal_cpu_time_budget ;
-  executing->budget_algorithm = normal_budget_algorithm ;
+  executing->CPU_budget = normal_cpu_budget;
   after_call_is_preemptible = executing->is_preemptible;
   executing->is_preemptible = normal_is_preemptible;
 
diff --git a/cpukit/rtems/src/taskconstruct.c b/cpukit/rtems/src/taskconstruct.c
index 6e03440..05fbf32 100644
--- a/cpukit/rtems/src/taskconstruct.c
+++ b/cpukit/rtems/src/taskconstruct.c
@@ -154,14 +154,17 @@ rtems_status_code _RTEMS_tasks_Create(
   attributes = _Attributes_Clear( attributes, ATTRIBUTES_NOT_SUPPORTED );
 
   memset( &thread_config, 0, sizeof( thread_config ) );
-  thread_config.budget_algorithm = _Modes_Is_timeslice( config->initial_modes ) ?
-    THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE
-      : THREAD_CPU_BUDGET_ALGORITHM_NONE,
   thread_config.isr_level =  _Modes_Get_interrupt_level( config->initial_modes );
   thread_config.name = config->name;
   thread_config.is_fp = _Attributes_Is_floating_point( attributes );
   thread_config.is_preemptible = _Modes_Is_preempt( config->initial_modes );
 
+  if ( _Modes_Is_timeslice( config->initial_modes ) ) {
+    thread_config.cpu_budget_operations = &_Thread_CPU_budget_reset_timeslice;
+  } else {
+    thread_config.cpu_budget_operations = NULL;
+  }
+
   /*
    *  Validate the RTEMS API priority and convert it to the core priority range.
    */
diff --git a/cpukit/rtems/src/taskmode.c b/cpukit/rtems/src/taskmode.c
index 96bed47..3300eaf 100644
--- a/cpukit/rtems/src/taskmode.c
+++ b/cpukit/rtems/src/taskmode.c
@@ -73,7 +73,7 @@ rtems_status_code rtems_task_mode(
 
   old_mode  = (executing->is_preemptible) ? RTEMS_PREEMPT : RTEMS_NO_PREEMPT;
 
-  if ( executing->budget_algorithm == THREAD_CPU_BUDGET_ALGORITHM_NONE )
+  if ( executing->CPU_budget.operations == NULL )
     old_mode |= RTEMS_NO_TIMESLICE;
   else
     old_mode |= RTEMS_TIMESLICE;
diff --git a/cpukit/score/src/mpci.c b/cpukit/score/src/mpci.c
index cb306c9..63a7eb1 100644
--- a/cpukit/score/src/mpci.c
+++ b/cpukit/score/src/mpci.c
@@ -144,7 +144,6 @@ static void _MPCI_Create_server( void )
   config.scheduler = &_Scheduler_Table[ 0 ];
   config.name = _Objects_Build_name( 'M', 'P', 'C', 'I' );
   config.priority = PRIORITY_PSEUDO_ISR;
-  config.budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE;
   config.is_fp = CPU_ALL_TASKS_ARE_FP;
   config.stack_size = _Stack_Minimum()
     + _MPCI_Configuration.extra_mpci_receive_server_stack
diff --git a/cpukit/score/src/schedulercbs.c b/cpukit/score/src/schedulercbs.c
index 8e5bf86..bedaf8e 100644
--- a/cpukit/score/src/schedulercbs.c
+++ b/cpukit/score/src/schedulercbs.c
@@ -21,10 +21,17 @@
 #endif
 
 #include <rtems/score/schedulercbsimpl.h>
+#include <rtems/score/statesimpl.h>
+#include <rtems/score/threadcpubudget.h>
 
-void _Scheduler_CBS_Budget_callout(
-  Thread_Control *the_thread
-)
+/**
+ * @brief Invoked when a limited time quantum is exceeded.
+ *
+ * This routine is invoked when a limited time quantum is exceeded.
+ *
+ * @param the_thread The thread that exceeded a limited time quantum.
+ */
+static void _Scheduler_CBS_Budget_callout( Thread_Control *the_thread )
 {
   Scheduler_CBS_Node      *node;
   Scheduler_CBS_Server_id  server_id;
@@ -52,6 +59,34 @@ void _Scheduler_CBS_Budget_callout(
   }
 }
 
+static void _Scheduler_CBS_Budget_at_tick( Thread_Control *the_thread )
+{
+  uint32_t budget_available;
+
+  if ( !the_thread->is_preemptible ) {
+    return;
+  }
+
+  if ( !_States_Is_ready( the_thread->current_state ) ) {
+    return;
+  }
+
+  budget_available = the_thread->CPU_budget.available;
+
+  if ( budget_available == 1 ) {
+    the_thread->CPU_budget.available = 0;
+    _Scheduler_CBS_Budget_callout ( the_thread );
+  } else {
+    the_thread->CPU_budget.available = budget_available - 1;
+  }
+}
+
+const Thread_CPU_budget_operations _Scheduler_CBS_Budget = {
+  .at_tick = _Scheduler_CBS_Budget_at_tick,
+  .at_context_switch = _Thread_CPU_budget_do_nothing,
+  .initialize = _Thread_CPU_budget_do_nothing
+};
+
 int _Scheduler_CBS_Initialize(void)
 {
   return SCHEDULER_CBS_OK;
diff --git a/cpukit/score/src/schedulercbsattachthread.c b/cpukit/score/src/schedulercbsattachthread.c
index 0cb59fa..d6c5b3b 100644
--- a/cpukit/score/src/schedulercbsattachthread.c
+++ b/cpukit/score/src/schedulercbsattachthread.c
@@ -64,9 +64,8 @@ int _Scheduler_CBS_Attach_thread (
 
   server->task_id = task_id;
 
-  the_thread->budget_callout   = _Scheduler_CBS_Budget_callout;
-  the_thread->budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_CALLOUT;
-  the_thread->is_preemptible   = true;
+  the_thread->is_preemptible = true;
+  the_thread->CPU_budget.operations = &_Scheduler_CBS_Budget;
 
   _ISR_lock_ISR_enable( &lock_context );
   return SCHEDULER_CBS_OK;
diff --git a/cpukit/score/src/schedulercbsdetachthread.c b/cpukit/score/src/schedulercbsdetachthread.c
index 687b378..5aa5eeb 100644
--- a/cpukit/score/src/schedulercbsdetachthread.c
+++ b/cpukit/score/src/schedulercbsdetachthread.c
@@ -28,10 +28,11 @@ int _Scheduler_CBS_Detach_thread (
   rtems_id                task_id
 )
 {
-  Scheduler_CBS_Server *server;
-  ISR_lock_Context      lock_context;
-  Thread_Control       *the_thread;
-  Scheduler_CBS_Node   *node;
+  Scheduler_CBS_Server               *server;
+  ISR_lock_Context                    lock_context;
+  Thread_Control                     *the_thread;
+  Scheduler_CBS_Node                 *node;
+  const Thread_CPU_budget_operations *cpu_budget_operations;
 
   if ( server_id >= _Scheduler_CBS_Maximum_servers ) {
     return SCHEDULER_CBS_ERROR_INVALID_PARAMETER;
@@ -58,9 +59,14 @@ int _Scheduler_CBS_Detach_thread (
 
   server->task_id = -1;
 
-  the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
-  the_thread->budget_callout   = the_thread->Start.budget_callout;
-  the_thread->is_preemptible   = the_thread->Start.is_preemptible;
+  the_thread->is_preemptible = the_thread->Start.is_preemptible;
+
+  cpu_budget_operations = the_thread->Start.cpu_budget_operations;
+  the_thread->CPU_budget.operations = cpu_budget_operations;
+
+  if ( cpu_budget_operations != NULL ) {
+    ( *cpu_budget_operations->initialize )( the_thread );
+  }
 
   _ISR_lock_ISR_enable( &lock_context );
   return SCHEDULER_CBS_OK;
diff --git a/cpukit/score/src/schedulercbsgetexecutiontime.c b/cpukit/score/src/schedulercbsgetexecutiontime.c
index 2870915..c8e999a 100644
--- a/cpukit/score/src/schedulercbsgetexecutiontime.c
+++ b/cpukit/score/src/schedulercbsgetexecutiontime.c
@@ -51,7 +51,7 @@ int _Scheduler_CBS_Get_execution_time (
   the_thread = _Thread_Get( server->task_id, &lock_context );
 
   if ( the_thread != NULL ) {
-    *exec_time = server->parameters.budget - the_thread->cpu_time_budget;
+    *exec_time = server->parameters.budget - the_thread->CPU_budget.available;
     _ISR_lock_ISR_enable( &lock_context );
   } else {
     *exec_time = server->parameters.budget;
diff --git a/cpukit/score/src/schedulercbsgetremainingbudget.c b/cpukit/score/src/schedulercbsgetremainingbudget.c
index 5cb299d..338fd56 100644
--- a/cpukit/score/src/schedulercbsgetremainingbudget.c
+++ b/cpukit/score/src/schedulercbsgetremainingbudget.c
@@ -50,7 +50,7 @@ int _Scheduler_CBS_Get_remaining_budget (
   the_thread = _Thread_Get( server->task_id, &lock_context );
 
   if ( the_thread != NULL ) {
-    *remaining_budget = the_thread->cpu_time_budget;
+    *remaining_budget = the_thread->CPU_budget.available;
     _ISR_lock_ISR_enable( &lock_context );
   } else {
     *remaining_budget = 0;
diff --git a/cpukit/score/src/schedulercbsreleasejob.c b/cpukit/score/src/schedulercbsreleasejob.c
index 376906b..27ca33a 100644
--- a/cpukit/score/src/schedulercbsreleasejob.c
+++ b/cpukit/score/src/schedulercbsreleasejob.c
@@ -38,7 +38,7 @@ void _Scheduler_CBS_Release_job(
 
   /* Budget replenishment for the next job. */
   if ( serv_info != NULL ) {
-    the_thread->cpu_time_budget = serv_info->parameters.budget;
+    the_thread->CPU_budget.available = serv_info->parameters.budget;
   }
 
   node->deadline_node = priority_node;
diff --git a/cpukit/score/src/schedulercbsunblock.c b/cpukit/score/src/schedulercbsunblock.c
index 700d7b1..ca98515 100644
--- a/cpukit/score/src/schedulercbsunblock.c
+++ b/cpukit/score/src/schedulercbsunblock.c
@@ -50,7 +50,7 @@ void _Scheduler_CBS_Unblock(
   if ( serv_info != NULL && ( priority & SCHEDULER_EDF_PRIO_MSB ) == 0 ) {
     time_t deadline = serv_info->parameters.deadline;
     time_t budget = serv_info->parameters.budget;
-    uint32_t deadline_left = the_thread->cpu_time_budget;
+    uint32_t deadline_left = the_thread->CPU_budget.available;
     Priority_Control budget_left = priority - _Watchdog_Ticks_since_boot;
 
     if ( deadline * budget_left > budget * deadline_left ) {
diff --git a/cpukit/score/src/schedulerdefaulttick.c b/cpukit/score/src/schedulerdefaulttick.c
deleted file mode 100644
index f4b6ba8..0000000
--- a/cpukit/score/src/schedulerdefaulttick.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * @file
- *
- * @ingroup RTEMSScoreScheduler
- *
- * @brief This source file contains the implementation of
- *   _Scheduler_default_Tick().
- */
-
-/*
- *  COPYRIGHT (c) 1989-2009.
- *  On-Line Applications Research Corporation (OAR).
- *
- *  The license and distribution terms for this file may be
- *  found in the file LICENSE in this distribution or at
- *  http://www.rtems.org/license/LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <rtems/score/schedulerimpl.h>
-#include <rtems/score/threadimpl.h>
-#include <rtems/score/smp.h>
-#include <rtems/config.h>
-
-void _Scheduler_default_Tick(
-  const Scheduler_Control *scheduler,
-  Thread_Control          *executing
-)
-{
-  (void) scheduler;
-
-  /*
-   *  If the thread is not preemptible or is not ready, then
-   *  just return.
-   */
-
-  if ( !executing->is_preemptible )
-    return;
-
-  if ( !_States_Is_ready( executing->current_state ) )
-    return;
-
-  /*
-   *  The cpu budget algorithm determines what happens next.
-   */
-
-  switch ( executing->budget_algorithm ) {
-    case THREAD_CPU_BUDGET_ALGORITHM_NONE:
-      break;
-
-    case THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE:
-    #if defined(RTEMS_SCORE_THREAD_ENABLE_EXHAUST_TIMESLICE)
-      case THREAD_CPU_BUDGET_ALGORITHM_EXHAUST_TIMESLICE:
-    #endif
-      if ( (int)(--executing->cpu_time_budget) <= 0 ) {
-
-        /*
-         *  A yield performs the ready chain mechanics needed when
-         *  resetting a timeslice.  If no other thread's are ready
-         *  at the priority of the currently executing thread, then the
-         *  executing thread's timeslice is reset.  Otherwise, the
-         *  currently executing thread is placed at the rear of the
-         *  FIFO for this priority and a new heir is selected.
-         */
-        _Thread_Yield( executing );
-        executing->cpu_time_budget =
-          rtems_configuration_get_ticks_per_timeslice();
-      }
-      break;
-
-    #if defined(RTEMS_SCORE_THREAD_ENABLE_SCHEDULER_CALLOUT)
-      case THREAD_CPU_BUDGET_ALGORITHM_CALLOUT:
-	if ( --executing->cpu_time_budget == 0 )
-	  (*executing->budget_callout)( executing );
-	break;
-    #endif
-  }
-}
diff --git a/cpukit/score/src/threadcreateidle.c b/cpukit/score/src/threadcreateidle.c
index 86e117e..83d9bdc 100644
--- a/cpukit/score/src/threadcreateidle.c
+++ b/cpukit/score/src/threadcreateidle.c
@@ -43,7 +43,6 @@ static void _Thread_Create_idle_for_CPU( Per_CPU_Control *cpu )
     config.scheduler,
     config.scheduler->maximum_priority
   );
-  config.budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE;
   config.name = _Objects_Build_name( 'I', 'D', 'L', 'E' );
   config.is_fp = CPU_IDLE_TASK_IS_FP;
   config.is_preemptible = true;
diff --git a/cpukit/score/src/threaddispatch.c b/cpukit/score/src/threaddispatch.c
index 1d317ad..a53c8de 100644
--- a/cpukit/score/src/threaddispatch.c
+++ b/cpukit/score/src/threaddispatch.c
@@ -275,7 +275,8 @@ void _Thread_Do_dispatch( Per_CPU_Control *cpu_self, ISR_Level level )
   executing = cpu_self->executing;
 
   do {
-    Thread_Control *heir;
+    Thread_Control                     *heir;
+    const Thread_CPU_budget_operations *cpu_budget_operations;
 
     level = _Thread_Preemption_intervention( executing, cpu_self, level );
     heir = _Thread_Get_heir_and_make_it_executing( cpu_self );
@@ -292,8 +293,12 @@ void _Thread_Do_dispatch( Per_CPU_Control *cpu_self, ISR_Level level )
      *  Since heir and executing are not the same, we need to do a real
      *  context switch.
      */
-    if ( heir->budget_algorithm == THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE )
-      heir->cpu_time_budget = rtems_configuration_get_ticks_per_timeslice();
+
+    cpu_budget_operations = heir->CPU_budget.operations;
+
+    if ( cpu_budget_operations != NULL ) {
+      ( *cpu_budget_operations->at_context_switch )( heir );
+    }
 
     _ISR_Local_enable( level );
 
diff --git a/cpukit/score/src/threadexhausttimeslice.c b/cpukit/score/src/threadexhausttimeslice.c
new file mode 100644
index 0000000..f3f6924
--- /dev/null
+++ b/cpukit/score/src/threadexhausttimeslice.c
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSScoreThread
+ *
+ * @brief This source file contains the definition of
+ *   ::_Thread_CPU_budget_exhaust_timeslice and the implementation of
+ *   _Thread_CPU_budget_do_nothing().
+ */
+
+/*
+ * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/score/threadcpubudget.h>
+
+void _Thread_CPU_budget_do_nothing( Thread_Control *the_thread )
+{
+  (void) the_thread;
+}
+
+const Thread_CPU_budget_operations _Thread_CPU_budget_exhaust_timeslice = {
+  .at_tick = _Thread_CPU_budget_consume_and_yield,
+  .at_context_switch = _Thread_CPU_budget_do_nothing,
+  .initialize = _Thread_CPU_budget_set_to_ticks_per_timeslice
+};
diff --git a/cpukit/score/src/threadinitialize.c b/cpukit/score/src/threadinitialize.c
index 81199a7..01ef479 100644
--- a/cpukit/score/src/threadinitialize.c
+++ b/cpukit/score/src/threadinitialize.c
@@ -274,13 +274,11 @@ static bool _Thread_Try_initialize(
    *  General initialization
    */
 
-  the_thread->is_fp                  = config->is_fp;
-  the_thread->cpu_time_budget        = config->cpu_time_budget;
-  the_thread->Start.isr_level        = config->isr_level;
-  the_thread->Start.is_preemptible   = config->is_preemptible;
-  the_thread->Start.budget_algorithm = config->budget_algorithm;
-  the_thread->Start.budget_callout   = config->budget_callout;
-  the_thread->Start.stack_free       = config->stack_free;
+  the_thread->is_fp                       = config->is_fp;
+  the_thread->Start.isr_level             = config->isr_level;
+  the_thread->Start.is_preemptible        = config->is_preemptible;
+  the_thread->Start.cpu_budget_operations = config->cpu_budget_operations;
+  the_thread->Start.stack_free            = config->stack_free;
 
   _Thread_Timer_initialize( &the_thread->Timer, cpu );
   _Thread_Initialize_scheduler_and_wait_nodes( the_thread, config );
diff --git a/cpukit/score/src/threadloadenv.c b/cpukit/score/src/threadloadenv.c
index fec2a61..4931742 100644
--- a/cpukit/score/src/threadloadenv.c
+++ b/cpukit/score/src/threadloadenv.c
@@ -26,6 +26,8 @@ void _Thread_Load_environment(
   Thread_Control *the_thread
 )
 {
+  const Thread_CPU_budget_operations *cpu_budget_operations;
+
 #if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
   if ( the_thread->Start.fp_context ) {
     the_thread->fp_context = the_thread->Start.fp_context;
@@ -34,8 +36,13 @@ void _Thread_Load_environment(
 #endif
 
   the_thread->is_preemptible   = the_thread->Start.is_preemptible;
-  the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
-  the_thread->budget_callout   = the_thread->Start.budget_callout;
+
+  cpu_budget_operations = the_thread->Start.cpu_budget_operations;
+  the_thread->CPU_budget.operations = cpu_budget_operations;
+
+  if ( cpu_budget_operations != NULL ) {
+    ( *cpu_budget_operations->initialize )( the_thread );
+  }
 
   _Context_Initialize(
     &the_thread->Registers,
diff --git a/cpukit/score/src/threadresettimeslice.c b/cpukit/score/src/threadresettimeslice.c
new file mode 100644
index 0000000..ed4d6fb
--- /dev/null
+++ b/cpukit/score/src/threadresettimeslice.c
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSScoreThread
+ *
+ * @brief This source file contains the definition of
+ *   ::_Thread_CPU_budget_reset_timeslice and the implementation of
+ *   _Thread_CPU_budget_consume_and_yield() and
+ *   _Thread_CPU_budget_set_to_ticks_per_timeslice().
+ */
+
+/*
+ * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+ * Copyright (C) 2010 Gedare Bloom  <gedare at rtems.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/score/threadcpubudget.h>
+#include <rtems/score/threadimpl.h>
+#include <rtems/score/statesimpl.h>
+#include <rtems/score/watchdogticks.h>
+
+void _Thread_CPU_budget_consume_and_yield( Thread_Control *the_thread )
+{
+  uint32_t budget_available;
+
+  if ( !the_thread->is_preemptible ) {
+    return;
+  }
+
+  if ( !_States_Is_ready( the_thread->current_state ) ) {
+    return;
+  }
+
+  budget_available = the_thread->CPU_budget.available;
+
+  if ( budget_available == 1 ) {
+    the_thread->CPU_budget.available = _Watchdog_Ticks_per_timeslice;
+
+    /*
+     *  A yield performs the ready chain mechanics needed when
+     *  resetting a timeslice.  If no other thread's are ready
+     *  at the priority of the currently executing thread, then the
+     *  executing thread's timeslice is reset.  Otherwise, the
+     *  currently executing thread is placed at the rear of the
+     *  FIFO for this priority and a new heir is selected.
+     */
+    _Thread_Yield( the_thread );
+  } else {
+    the_thread->CPU_budget.available = budget_available - 1;
+  }
+}
+
+void _Thread_CPU_budget_set_to_ticks_per_timeslice(
+  Thread_Control *the_thread
+)
+{
+  the_thread->CPU_budget.available = _Watchdog_Ticks_per_timeslice;
+}
+
+const Thread_CPU_budget_operations _Thread_CPU_budget_reset_timeslice = {
+  .at_tick = _Thread_CPU_budget_consume_and_yield,
+  .at_context_switch = _Thread_CPU_budget_set_to_ticks_per_timeslice,
+  .initialize = _Thread_CPU_budget_set_to_ticks_per_timeslice
+};
diff --git a/cpukit/score/src/threadrestart.c b/cpukit/score/src/threadrestart.c
index 2240d8f..15e141b 100644
--- a/cpukit/score/src/threadrestart.c
+++ b/cpukit/score/src/threadrestart.c
@@ -217,10 +217,6 @@ static Thread_Life_state _Thread_Change_life_locked(
     _Thread_Is_life_change_allowed( state )
       && _Thread_Is_life_changing( state )
   ) {
-    the_thread->is_preemptible   = the_thread->Start.is_preemptible;
-    the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
-    the_thread->budget_callout   = the_thread->Start.budget_callout;
-
     _Thread_Add_post_switch_action(
       the_thread,
       &the_thread->Life.Action,
diff --git a/cpukit/score/src/watchdogtick.c b/cpukit/score/src/watchdogtick.c
index e77ca4e..aa2efc3 100644
--- a/cpukit/score/src/watchdogtick.c
+++ b/cpukit/score/src/watchdogtick.c
@@ -62,11 +62,13 @@ void _Watchdog_Do_tickle(
 
 void _Watchdog_Tick( Per_CPU_Control *cpu )
 {
-  ISR_lock_Context  lock_context;
-  Watchdog_Header  *header;
-  Watchdog_Control *first;
-  uint64_t          ticks;
-  struct timespec   now;
+  ISR_lock_Context                    lock_context;
+  Watchdog_Header                    *header;
+  Watchdog_Control                   *first;
+  uint64_t                            ticks;
+  struct timespec                     now;
+  Thread_Control                     *executing;
+  const Thread_CPU_budget_operations *cpu_budget_operations;
 
   if ( _Per_CPU_Is_boot_processor( cpu ) ) {
     ++_Watchdog_Ticks_since_boot;
@@ -122,5 +124,16 @@ void _Watchdog_Tick( Per_CPU_Control *cpu )
 
   _ISR_lock_Release_and_ISR_enable( &cpu->Watchdog.Lock, &lock_context );
 
-  _Scheduler_Tick( cpu );
+  /*
+   * Each online processor has at least an idle thread as the executing thread
+   * even in case it has currently no scheduler assigned.  Clock interrupts on
+   * processors which are not online would be a severe bug of the Clock Driver.
+   */
+  executing = _Per_CPU_Get_executing( cpu );
+  _Assert( executing != NULL );
+  cpu_budget_operations = executing->CPU_budget.operations;
+
+  if ( cpu_budget_operations != NULL ) {
+    ( *cpu_budget_operations->at_tick )( executing );
+  }
 }
diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml
index 2b25b5d..063917b 100644
--- a/spec/build/cpukit/librtemscpu.yml
+++ b/spec/build/cpukit/librtemscpu.yml
@@ -413,6 +413,7 @@ install:
   - cpukit/include/rtems/score/status.h
   - cpukit/include/rtems/score/sysstate.h
   - cpukit/include/rtems/score/thread.h
+  - cpukit/include/rtems/score/threadcpubudget.h
   - cpukit/include/rtems/score/threaddispatch.h
   - cpukit/include/rtems/score/threadidledata.h
   - cpukit/include/rtems/score/threadimpl.h
@@ -1506,7 +1507,6 @@ source:
 - cpukit/score/src/schedulerdefaultreleasejob.c
 - cpukit/score/src/schedulerdefaultschedule.c
 - cpukit/score/src/schedulerdefaultstartidle.c
-- cpukit/score/src/schedulerdefaulttick.c
 - cpukit/score/src/scheduleredf.c
 - cpukit/score/src/scheduleredfblock.c
 - cpukit/score/src/scheduleredfchangepriority.c
@@ -1544,6 +1544,7 @@ source:
 - cpukit/score/src/threadentryadaptoridle.c
 - cpukit/score/src/threadentryadaptornumeric.c
 - cpukit/score/src/threadentryadaptorpointer.c
+- cpukit/score/src/threadexhausttimeslice.c
 - cpukit/score/src/threadget.c
 - cpukit/score/src/threadgetcputimeused.c
 - cpukit/score/src/threadgetcputimeusedafterreset.c
@@ -1562,6 +1563,7 @@ source:
 - cpukit/score/src/threadqgetnameandid.c
 - cpukit/score/src/threadqops.c
 - cpukit/score/src/threadqtimeout.c
+- cpukit/score/src/threadresettimeslice.c
 - cpukit/score/src/threadrestart.c
 - cpukit/score/src/threadscheduler.c
 - cpukit/score/src/threadselfid.c
diff --git a/testsuites/sptests/sptimecounter01/init.c b/testsuites/sptests/sptimecounter01/init.c
index dc601d7..23671e3 100644
--- a/testsuites/sptests/sptimecounter01/init.c
+++ b/testsuites/sptests/sptimecounter01/init.c
@@ -23,6 +23,7 @@
 #include <bsp/bootcard.h>
 
 #include <rtems/score/timecounterimpl.h>
+#include <rtems/score/percpu.h>
 #include <rtems/score/todimpl.h>
 #include <rtems/timecounter.h>
 #include <rtems/bsd.h>
@@ -42,6 +43,8 @@ typedef struct {
 
 static test_context test_instance;
 
+static Thread_Control executing;
+
 static uint32_t test_get_timecount(struct timecounter *tc)
 {
   test_context *ctx;
@@ -106,12 +109,17 @@ void boot_card(const char *cmdline)
   struct bintime bt;
   struct timeval tv;
   struct timespec ts;
+  Per_CPU_Control *cpu_self;
 
   ctx = &test_instance;
   tc = &ctx->tc;
 
   TEST_BEGIN();
 
+  cpu_self = _Per_CPU_Get();
+  cpu_self->executing = &executing;
+  cpu_self->heir = &executing;
+
   assert(time(NULL) == TOD_SECONDS_1970_THROUGH_1988);
 
   rtems_bsd_bintime(&bt);



More information about the vc mailing list