[PATCH] score: Introduce CPU budget operations

Sebastian Huber sebastian.huber at embedded-brains.de
Wed Oct 27 13:53:22 UTC 2021


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 -------
 .../include/rtems/score/schedulerpriority.h   |   1 -
 .../score/schedulerpriorityaffinitysmp.h      |   1 -
 .../rtems/score/schedulerprioritysmp.h        |   1 -
 cpukit/include/rtems/score/schedulersimple.h  |   1 -
 .../include/rtems/score/schedulersimplesmp.h  |   1 -
 .../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         |  42 ++++----
 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/schedulercbs.c               |  41 ++++++-
 cpukit/score/src/schedulercbsattachthread.c   |   6 +-
 cpukit/score/src/schedulercbsdetachthread.c   |  20 ++--
 .../score/src/schedulercbsgetexecutiontime.c  |   2 +-
 .../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     |  47 ++++++++
 cpukit/score/src/threadinitialize.c           |  12 +--
 cpukit/score/src/threadloadenv.c              |  11 +-
 cpukit/score/src/threadresettimeslice.c       |  96 +++++++++++++++++
 cpukit/score/src/threadrestart.c              |   4 -
 cpukit/score/src/watchdogtick.c               |  25 +++--
 spec/build/cpukit/librtemscpu.yml             |   3 +-
 testsuites/sptests/sptimecounter01/init.c     |   8 ++
 42 files changed, 517 insertions(+), 332 deletions(-)
 create mode 100644 cpukit/include/rtems/score/threadcpubudget.h
 delete mode 100644 cpukit/score/src/schedulerdefaulttick.c
 create mode 100644 cpukit/score/src/threadexhausttimeslice.c
 create mode 100644 cpukit/score/src/threadresettimeslice.c

diff --git a/cpukit/include/rtems/posix/pthreadimpl.h b/cpukit/include/rtems/posix/pthreadimpl.h
index 723b20e8d2..0dc7d9dac5 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 924e12fbee..8fdab263f1 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 579621d5f5..2fd1fc567a 100644
--- a/cpukit/include/rtems/score/scheduler.h
+++ b/cpukit/include/rtems/score/scheduler.h
@@ -251,9 +251,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 *,
@@ -597,20 +594,6 @@ void _Scheduler_default_Cancel_job(
   Thread_queue_Context    *queue_context
 );
 
-/**
- * @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.
  *
diff --git a/cpukit/include/rtems/score/schedulercbs.h b/cpukit/include/rtems/score/schedulercbs.h
index 4b7efc8340..8a9a49ccd9 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 9e643b93eb..258563217f 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 89d7425ef5..2c73c5535a 100644
--- a/cpukit/include/rtems/score/scheduleredfsmp.h
+++ b/cpukit/include/rtems/score/scheduleredfsmp.h
@@ -139,7 +139,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 0c5f90f19f..c2c02b1cd9 100644
--- a/cpukit/include/rtems/score/schedulerimpl.h
+++ b/cpukit/include/rtems/score/schedulerimpl.h
@@ -540,44 +540,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.
  *
diff --git a/cpukit/include/rtems/score/schedulerpriority.h b/cpukit/include/rtems/score/schedulerpriority.h
index e0991f5e31..1ddbb4a8cc 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 4d726e3c20..d77629b39d 100644
--- a/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h
+++ b/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h
@@ -75,7 +75,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 e6303631b1..21916647bc 100644
--- a/cpukit/include/rtems/score/schedulerprioritysmp.h
+++ b/cpukit/include/rtems/score/schedulerprioritysmp.h
@@ -104,7 +104,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 63310e9796..15471a6498 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 95168dee9f..3b6f43869e 100644
--- a/cpukit/include/rtems/score/schedulersimplesmp.h
+++ b/cpukit/include/rtems/score/schedulersimplesmp.h
@@ -85,7 +85,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 7e6d631139..8db3ae8634 100644
--- a/cpukit/include/rtems/score/schedulerstrongapa.h
+++ b/cpukit/include/rtems/score/schedulerstrongapa.h
@@ -171,7 +171,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 aff2f58d77..4c8a97c0f7 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 0000000000..bcbaa11bdb
--- /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 7c4890dc8f..6ecae7d9ac 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 eba26d4932..7e8202a17c 100644
--- a/cpukit/posix/src/psxtransschedparam.c
+++ b/cpukit/posix/src/psxtransschedparam.c
@@ -23,22 +23,25 @@
 #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 ( operations == &_POSIX_Threads_Sporadic_budget ) {
+    return SCHED_SPORADIC;
+  }
+
+  return SCHED_OTHER;
 }
 
 int _POSIX_Thread_Translate_sched_param(
@@ -47,23 +50,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 +80,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 9474d07032..093ad5cfe1 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 5572fb98a5..aa34185264 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 a82d79c715..406ae8e7bb 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 1c207e7887..165e1d86a8 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 72407e2b01..6ce59f2e74 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 6e03440aed..05fbf32ff5 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 96bed470f4..3300eafa28 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/schedulercbs.c b/cpukit/score/src/schedulercbs.c
index 8e5bf86f27..bedaf8e1da 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 0cb59fa2e8..dbdb1134be 100644
--- a/cpukit/score/src/schedulercbsattachthread.c
+++ b/cpukit/score/src/schedulercbsattachthread.c
@@ -64,9 +64,9 @@ 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;
+  ( *_Scheduler_CBS_Budget.initialize )( the_thread );
 
   _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 687b37804f..5aa5eeb7a2 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 28709151c0..c8e999a788 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 5cb299d67e..338fd56190 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 376906b996..27ca33ad56 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 700d7b1202..ca985150fb 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 f4b6ba8578..0000000000
--- 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 86e117e70f..83d9bdcd8d 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 1d317ad2b1..a53c8de2b0 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 0000000000..533bddeed1
--- /dev/null
+++ b/cpukit/score/src/threadexhausttimeslice.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSScoreThread
+ *
+ * @brief This source file contains the definition of
+ *   ::_Thread_CPU_budget_exhaust_timeslice.
+ */
+
+/*
+ * 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>
+
+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 81199a7044..01ef479537 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 fec2a616d8..49317421b3 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 0000000000..f6fa76b4ec
--- /dev/null
+++ b/cpukit/score/src/threadresettimeslice.c
@@ -0,0 +1,96 @@
+/* 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(), _Thread_CPU_budget_do_nothing(),
+ *   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_do_nothing( Thread_Control *the_thread )
+{
+  (void) the_thread;
+}
+
+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 2240d8f713..15e141b51e 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 e77ca4e1e1..aa2efc3626 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 2d3a4255c6..386787baed 100644
--- a/spec/build/cpukit/librtemscpu.yml
+++ b/spec/build/cpukit/librtemscpu.yml
@@ -1505,7 +1505,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
@@ -1543,6 +1542,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
@@ -1561,6 +1561,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 dc601d7acb..23671e3ac8 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);
-- 
2.26.2



More information about the devel mailing list