From: quzar <qu...@us...> - 2024-05-18 01:09:50
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "A pseudo Operating System for the Dreamcast.". The branch, master has been updated via caf28811f7dd3959565f07ce6dd0bb5e4fa6cde4 (commit) from 426aea1b7727de803f1532dd86e696d2c0914ccd (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit caf28811f7dd3959565f07ce6dd0bb5e4fa6cde4 Author: Falco Girgis <gyr...@gm...> Date: Fri May 17 20:08:49 2024 -0500 Per-thread CPU time implementation (#506) * Initial per-thread CPU time implementation - Maintaining and updating threads' CPU time upon context switches - Printing out time and % of whole when doing thd_pslist() to print - Still seem to have an issue with not having 100% of the CPU time accounted for - Also accidentally snuck in a bunch of general clean-up * Thread-specific CPU time is now WORKING! 1) Fixed a bug with an active thread getting rescheduled which caused it to leak out CPU time 2) Implemented CLOCK_THREAD_CPUTIME_ID for clock_gettime() 3) Still need to make thd_plist() printing prettier for CPU time 4) Enabled _POSIX_THREAD_CPUTIME in time.h - thd_current() -> thd_get_current() in clock_gettime.c. * Got thd_plist() printing prettily finally... - But I really hope that's not just based on how many spaces I have my own terminal configured to represent tabs as... 1) Cleaned up the rest of the threading API to be more "proper" and idiomatic C. No, I shouldn't have broken backwards compat. 2) Added Colton's copyright info, because he did a lot of good TLS work last year and forgot to add it to thread.h and thread.c 3) Tweaked formatting more for thd_pslist()... looks good on my terminal... 4) Cleaned up Doxygen, added everything to kthread group 5) Added comments to initial CPU time synchronization during main thread initialization * Updated thread.h todo list + CHANGELOG. * Apply suggestions from code review Co-authored-by: Andy Barajas <and...@gm...> * Upgraded/fixed _times_r()/clock() based on C spec. - clock() is supposed to measure ACTIVE PROCESS TIME, but we were measuring Wall time, previously. - clock() now attempts to use the performance counters to get active process CPU ticks, but will fall back to the TMU units and wall time. - clock() now gracefully returns -1, per the spec, when the number of ticks can no longer be represented by a clock_t. - Main thread cpu time initialization could also share common update function - Had to tweak the control flow for the display loop grabbing each thread's CPU time to take into consideration how much time is elapsing during printing each value - Found a bug in performance counter stop, which was potentially clearing its configuration and wasn't stopping it. - Added missing performance counter API function: perf_cntr_resume() for non-clearing start after a stop. - Updated general-threading test to show some of the cool new stats. * Apply suggestions from code review Co-authored-by: Donald Haase <qu...@ya...> --------- Co-authored-by: Andy Barajas <and...@gm...> Co-authored-by: darcagn <da...@pr...> Co-authored-by: Donald Haase <qu...@ya...> ----------------------------------------------------------------------- Summary of changes: doc/CHANGELOG | 1 + .../threading/general/general_threading_test.c | 15 +- include/kos/thread.h | 216 +++++++++++++-------- include/kos/time.h | 4 +- kernel/arch/dreamcast/include/dc/perfctr.h | 16 +- kernel/arch/dreamcast/kernel/perfctr.c | 13 +- kernel/libc/newlib/newlib_times.c | 22 ++- kernel/libc/posix/clock_gettime.c | 18 +- kernel/thread/thread.c | 63 ++++-- 9 files changed, 258 insertions(+), 110 deletions(-) diff --git a/doc/CHANGELOG b/doc/CHANGELOG index d367f2cb..3471ca46 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -225,6 +225,7 @@ KallistiOS version 2.1.0 ----------------------------------------------- - *** Add support for worker threads [PC] - DC Added new System Calls module [AB] - DC Add timer_spin_delay_{us,ns} and use them in scif-spi [PC] +- *** Added thread support for tracking CPU time + updated clock_gettime() [FG] KallistiOS version 2.0.0 ----------------------------------------------- - DC Broadband Adapter driver fixes [Megan Potter == MP] diff --git a/examples/dreamcast/basic/threading/general/general_threading_test.c b/examples/dreamcast/basic/threading/general/general_threading_test.c index 248494d8..a8afbcf5 100644 --- a/examples/dreamcast/basic/threading/general/general_threading_test.c +++ b/examples/dreamcast/basic/threading/general/general_threading_test.c @@ -2,7 +2,8 @@ general_threading_test.c - (c)2000-2002 Megan Potter + Copyright (C) 2000-2002 Megan Potter + Copyright (C) 2024 Falco Girgis A simple thread example @@ -27,6 +28,8 @@ void *thd_0(void *v) { for(x = 0; x < 320; x++) vram_s[y * 640 + x] = (((x * x) + (y * y)) & 0x1f) << 11; + thd_pslist(printf); + printf("Thread 0 finished\n"); return NULL; } @@ -43,6 +46,9 @@ void *thd_1(void *v) { printf("Thread 1 waiting:\n"); thd_sleep(5000); + + thd_pslist(printf); + printf("Thread 1 exiting\n"); return NULL; } @@ -61,6 +67,9 @@ void *thd_2(void *v) { printf("sem_wait_timed returns %d\n", sem_wait_timed(&sem, 200)); printf("sem_wait_timed returns %d\n", sem_wait_timed(&sem, 200)); printf("sem_wait_timed returns %d\n", sem_wait_timed(&sem, 200)); + + thd_pslist(printf); + printf("Thread 2 exiting\n"); return NULL; } @@ -106,7 +115,7 @@ int main(int argc, char **argv) { (cont_btn_callback_t)arch_exit); /* Print a banner */ - printf("KOS 1.1.x thread program:\n"); + printf("KOS 2.0.x thread program:\n"); /* Create a semaphore for timing purposes */ sem_init(&sem, 1); @@ -189,6 +198,8 @@ int main(int argc, char **argv) { cond_broadcast(&cv); mutex_unlock(&mut); + thd_pslist(printf); + for(i = 0; i < 10; i++) thd_join(t3[i], NULL); diff --git a/include/kos/thread.h b/include/kos/thread.h index 0649f0c1..d8cfba6c 100644 --- a/include/kos/thread.h +++ b/include/kos/thread.h @@ -15,9 +15,6 @@ This file contains the interface to the threading system of KOS. Timer interrupts are used to reschedule threads within the system. - \author Megan Potter - \author Lawrence Sebald - \see arch/timer.h \see kos/genwait.h \see kos/mutex.h @@ -26,6 +23,14 @@ \see kos/rwsem.h \see kos/sem.h \see kos/tls.h + + \todo + - Remove deprecated thread mode API + - Remove global extern pointer to current thread + + \author Megan Potter + \author Lawrence Sebald + \author Falco Girgis */ #ifndef __KOS_THREAD_H @@ -39,7 +44,9 @@ __BEGIN_DECLS #include <arch/irq.h> #include <sys/queue.h> #include <sys/reent.h> + #include <stdint.h> +#include <stdbool.h> /** \defgroup kthreads Kernel \brief KOS Native Kernel Threading API @@ -66,6 +73,8 @@ __BEGIN_DECLS considered detached threads. \sa semaphore_t, mutex_t, kthread_once_t, kthread_key_t, rw_semaphore_t + + @{ */ /** \brief Process ID @@ -110,7 +119,36 @@ TAILQ_HEAD(ktqueue, kthread); LIST_HEAD(ktlist, kthread); /* \endcond */ -/** \brief Control Block Header +/** \name Thread flag values + \brief Flags for kthread_flags_t + + These are possible values for the flags field on the kthread_t structure. + These can be ORed together. + + @{ +*/ +#define THD_DEFAULTS 0 /**< \brief Defaults: no flags */ +#define THD_USER 1 /**< \brief Thread runs in user mode */ +#define THD_QUEUED 2 /**< \brief Thread is in the run queue */ +#define THD_DETACHED 4 /**< \brief Thread is detached */ +/** @} */ + +/** \brief Kernel thread flags type */ +typedef uint8_t kthread_flags_t; + +/** \brief Kernel thread state + + Each thread in the system is in exactly one of this set of states. +*/ +typedef enum kthread_state { + STATE_ZOMBIE = 0x0000, /**< \brief Waiting to die */ + STATE_RUNNING = 0x0001, /**< \brief Process is "current" */ + STATE_READY = 0x0002, /**< \brief Ready to be scheduled */ + STATE_WAIT = 0x0003, /**< \brief Blocked on a genwait */ + STATE_FINISHED = 0x0004 /**< \brief Finished execution */ +} kthread_state_t; + +/** \brief Thread Control Block Header Header preceding the static TLS data segments as defined by the SH-ELF TLS ABI (version 1). This is what the thread pointer @@ -126,8 +164,6 @@ typedef struct tcbhead { Each thread has one of these structures assigned to it, which holds all the data associated with the thread. There are various functions to manipulate the data in here, so you shouldn't generally do so manually. - - \headerfile kos/thread.h */ typedef __attribute__((aligned(32))) struct kthread { /** \brief Register store -- used to save thread context. */ @@ -148,20 +184,22 @@ typedef __attribute__((aligned(32))) struct kthread { /** \brief Static priority: 0..PRIO_MAX (higher means lower priority). */ prio_t prio; - /** \brief Thread flags. - \see thd_flags */ - uint32_t flags; + /** \brief Thread flags. */ + kthread_flags_t flags; - /** \brief Process state. - \see thd_states */ - int state; + /** \brief Process state */ + kthread_state_t state; /** \brief Generic wait target, if waiting. - \see kos/genwait.h */ + + \see kos/genwait.h + */ void *wait_obj; /** \brief Generic wait message, if waiting. - \see kos/genwait.h */ + + \see kos/genwait.h + */ const char *wait_msg; /** \brief Wait timeout callback. @@ -174,24 +212,36 @@ typedef __attribute__((aligned(32))) struct kthread { void (*wait_callback)(void *obj); /** \brief Next scheduled time. + This value is used for sleep and timed block operations. This value is in milliseconds since the start of timer_ms_gettime(). This should be - enough for something like 2 million years of wait time. ;) */ + enough for something like 2 million years of wait time. ;) + */ uint64_t wait_timeout; + /** \brief Per-Thread CPU Time. */ + struct { + uint64_t scheduled; /**< \brief time when the thread became active */ + uint64_t total; /**< \brief total running CPU time for thread */ + } cpu_time; + /** \brief Thread label. - This value is used when printing out a user-readable process listing. */ + + This value is used when printing out a user-readable process listing. + */ char label[KTHREAD_LABEL_SIZE]; /** \brief Current file system path. */ char pwd[KTHREAD_PWD_SIZE]; /** \brief Thread private stack. - This should be a pointer to the base of a stack page. */ - uint32_t *stack; + + This should be a pointer to the base of a stack page. + */ + void *stack; /** \brief Size of the thread's stack, in bytes. */ - uint32_t stack_size; + size_t stack_size; /** \brief Thread errno variable. */ int thd_errno; @@ -200,45 +250,21 @@ typedef __attribute__((aligned(32))) struct kthread { struct _reent thd_reent; /** \brief OS-level thread-local storage. - \see kos/tls.h */ + + \see kos/tls.h + */ struct kthread_tls_kv_list tls_list; /** \brief Compiler-level thread-local storage. */ tcbhead_t* tcbhead; /** \brief Return value of the thread function. - This is only used in joinable threads. */ + + This is only used in joinable threads. + */ void *rv; } kthread_t; -/** \name Thread flag values - \brief kthread_t::flags values - - These are possible values for the flags field on the kthread_t structure. - These can be ORed together. - - @{ -*/ -#define THD_DEFAULTS 0 /**< \brief Defaults: no flags */ -#define THD_USER 1 /**< \brief Thread runs in user mode */ -#define THD_QUEUED 2 /**< \brief Thread is in the run queue */ -#define THD_DETACHED 4 /**< \brief Thread is detached */ -/** @} */ - -/** \name Thread states - \brief kthread_t::state values - - Each thread in the system is in exactly one of this set of states. - - @{ -*/ -#define STATE_ZOMBIE 0x0000 /**< \brief Waiting to die */ -#define STATE_RUNNING 0x0001 /**< \brief Process is "current" */ -#define STATE_READY 0x0002 /**< \brief Ready to be scheduled */ -#define STATE_WAIT 0x0003 /**< \brief Blocked on a genwait */ -#define STATE_FINISHED 0x0004 /**< \brief Finished execution */ -/** @} */ - /** \brief Thread creation attributes. This structure allows you to specify the various attributes for a thread to @@ -253,10 +279,10 @@ typedef __attribute__((aligned(32))) struct kthread { */ typedef struct kthread_attr { /** \brief 1 for a detached thread. */ - int create_detached; + bool create_detached; /** \brief Set the size of the stack to be created. */ - uint32_t stack_size; + size_t stack_size; /** \brief Pre-allocate a stack for the thread. \note If you use this attribute, you must also set stack_size. */ @@ -269,18 +295,19 @@ typedef struct kthread_attr { const char *label; } kthread_attr_t; -/** \name Threading system modes - \brief kthread mode values +/** \brief kthread mode values + + \deprecated + Only preemptive scheduling is still supported! The threading system will always be in one of the following modes. This represents either pre-emptive scheduling or an un-initialized state. - - @{ */ -#define THD_MODE_NONE -1 /**< \brief Threads not running */ -#define THD_MODE_COOP 0 /**< \brief Cooperative mode \deprecated */ -#define THD_MODE_PREEMPT 1 /**< \brief Preemptive threading mode */ -/** @} */ +typedef enum kthread_mode { + THD_MODE_NONE = -1, /**< \brief Threads not running */ + THD_MODE_COOP = 0, /**< \brief Cooperative mode \deprecated */ + THD_MODE_PREEMPT = 1 /**< \brief Preemptive threading mode */ +} kthread_mode_t; /** \cond The currently executing thread -- Do not manipulate directly! */ extern kthread_t *thd_current; @@ -333,13 +360,13 @@ kthread_t *thd_by_tid(tid_t tid); of its priority group. Generally, you will not have to do this manually. \param t The thread to queue. - \param front_of_line Set to 1 to put this thread in front of other - threads of the same priority, 0 to put it behind the - other threads (normal behavior). + \param front_of_line Set to true to put this thread in front of other + threads of the same priority, false to put it + behind the other threads (normal behavior). \sa thd_remove_from_runnable */ -void thd_add_to_runnable(kthread_t *t, int front_of_line); +void thd_add_to_runnable(kthread_t *t, bool front_of_line); /** \brief Removes a thread from the runnable queue, if it's there. \relatesalso kthread_t @@ -364,8 +391,8 @@ int thd_remove_from_runnable(kthread_t *thd); routine completes if the thread is created detached, otherwise you must join the thread with thd_join() to clean up after it. - \param detach Set to 1 to create a detached thread. Set to 0 to - create a joinable thread. + \param detach Set to true to create a detached thread. Set to + false to create a joinable thread. \param routine The function to call in the new thread. \param param A parameter to pass to the function called. @@ -373,9 +400,9 @@ int thd_remove_from_runnable(kthread_t *thd); \sa thd_create_ex, thd_destroy */ -kthread_t *thd_create(int detach, void *(*routine)(void *param), void *param); +kthread_t *thd_create(bool detach, void *(*routine)(void *param), void *param); -/** \brief Create a new thread with the specified set of attributes. +/** \brief Create a new thread with the specified set of attributes. \relatesalso kthread_t This function creates a new kernel thread with the specified set of @@ -433,12 +460,12 @@ void thd_exit(void *rv) __noreturn; thd_current. Set 'now' to non-zero if you want to use a particular system time for checking timeouts. - \param front_of_line Set to 0, unless you have a good reason not to. + \param front_of_line Set to false, unless you have a good reason not to. \param now Set to 0, unless you have a good reason not to. \sa thd_schedule_next */ -void thd_schedule(int front_of_line, uint64_t now); +void thd_schedule(bool front_of_line, uint64_t now); /** \brief Force a given thread to the front of the queue. \relatesalso kthread_t @@ -446,6 +473,8 @@ void thd_schedule(int front_of_line, uint64_t now); This function promotes the given thread to be the next one that will be swapped in by the scheduler. This function is only callable inside an interrupt context (it simply returns otherwise). + + \param thd The thread to schedule next. */ void thd_schedule_next(kthread_t *thd); @@ -464,9 +493,12 @@ void thd_pass(void); at least the given number of milliseconds. If another thread is running, it will likely sleep longer. + \note + When \p ms is given a value of `0`, this is equivalent to thd_pass(). + \param ms The number of milliseconds to sleep. */ -void thd_sleep(int ms); +void thd_sleep(unsigned ms); /** \brief Set a thread's priority value. \relatesalso kthread_t @@ -572,20 +604,41 @@ int *thd_get_errno(kthread_t *thd); */ struct _reent *thd_get_reent(kthread_t *thd); + +/** \brief Retrieves the thread's elapsed CPU time + \relatesalso kthread_t + + Returns the amount of active CPU time the thread has consumed in + nanoseconds. + + \warning + The implementation uses perf_cntr_timer_ns() internally when maintaining + this CPU time, so disabling or clearing the nanosecond timer will + interfere with this time keeping. + + \param thd The thead to retrieve the CPU time for + + \retval Total utilized CPU time in nanoseconds OR + 0 if the nanosecond timer of the performance + counters has been disturbed. +*/ +uint64_t thd_get_cpu_time(kthread_t *thd); + /** \brief Change threading modes. This function changes the current threading mode of the system. With preemptive threading being the only mode. \deprecated - This is now deprecated. + This is now deprecated \param mode One of the THD_MODE values. + \return The old mode of the threading system. \sa thd_get_mode */ -int thd_set_mode(int mode) __deprecated; +int thd_set_mode(kthread_mode_t mode) __deprecated; /** \brief Fetch the current threading mode. @@ -598,7 +651,7 @@ int thd_set_mode(int mode) __deprecated; \sa thd_set_mode */ -int thd_get_mode(void) __deprecated; +kthread_mode_t thd_get_mode(void) __deprecated; /** \brief Set the scheduler's frequency. @@ -652,7 +705,7 @@ int thd_join(kthread_t *thd, void **value_ptr); \return 0 on success or less than 0 if the thread is non-existent or already detached. - \sa thd_join() + \sa thd_join() */ int thd_detach(kthread_t *thd); @@ -690,27 +743,32 @@ int thd_pslist(int (*pf)(const char *fmt, ...)); */ int thd_pslist_queue(int (*pf)(const char *fmt, ...)); -/** \brief Initialize the threading system. +/** \cond INTERNAL */ +/** \brief Initialize the threading system. + This is normally done for you by default when KOS starts. This will also initialize all the various synchronization primitives. - \retval -1 If threads are already initialized. \retval 0 On success. - \sa thd_shutdown */ int thd_init(void); -/** \brief Shutdown the threading system. +/** \brief Shutdown the threading system. + This is done for you by the normal shutdown procedure of KOS. This will also shutdown all the various synchronization primitives. - ...<truncated>... hooks/post-receive -- A pseudo Operating System for the Dreamcast. |