From 04c90835ac4db26b510d09f0f117c89e96c03d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Fri, 13 Dec 2024 17:15:55 +0100 Subject: [PATCH] POSIX: add pthread_getcpuclockid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #15615 Change-Id: I6b34f97464fac97d0b339fa338cfc5df34536080 Reviewed-on: https://review.haiku-os.org/c/haiku/+/8681 Reviewed-by: Jérôme Duval Reviewed-by: waddlesplash Tested-by: Commit checker robot --- headers/posix/pthread.h | 1 + headers/private/kernel/UserTimer.h | 1 + headers/private/system/syscall_clock_info.h | 15 ++++ headers/private/system/syscalls.h | 1 + src/system/kernel/UserTimer.cpp | 80 +++++++++++++++++-- .../libroot/posix/time/clock_support.cpp | 28 +++++-- src/tests/system/libroot/posix/Jamfile | 1 + .../libroot/posix/pthread_clock_test.cpp | 39 +++++++++ 8 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 headers/private/system/syscall_clock_info.h create mode 100644 src/tests/system/libroot/posix/pthread_clock_test.cpp diff --git a/headers/posix/pthread.h b/headers/posix/pthread.h index 106838ecb2..d3bed57fec 100644 --- a/headers/posix/pthread.h +++ b/headers/posix/pthread.h @@ -196,6 +196,7 @@ extern int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); extern int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); +extern int pthread_getcpuclockid(pthread_t thread_id, clockid_t* clock_id); /* thread attributes functions */ extern int pthread_attr_destroy(pthread_attr_t *attr); diff --git a/headers/private/kernel/UserTimer.h b/headers/private/kernel/UserTimer.h index ca880be9d4..66bcddc9e3 100644 --- a/headers/private/kernel/UserTimer.h +++ b/headers/private/kernel/UserTimer.h @@ -261,6 +261,7 @@ void user_timer_check_team_user_timers(Team* team); status_t _user_get_clock(clockid_t clockID, bigtime_t* _time); status_t _user_set_clock(clockid_t clockID, bigtime_t time); +status_t _user_get_cpuclockid(thread_id id, int32 which, clockid_t* _clockID); int32 _user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags, const struct sigevent* event, diff --git a/headers/private/system/syscall_clock_info.h b/headers/private/system/syscall_clock_info.h new file mode 100644 index 0000000000..fcb9a8124b --- /dev/null +++ b/headers/private/system/syscall_clock_info.h @@ -0,0 +1,15 @@ +/* + * Copyright 2024, Jérôme Duval, jerome.duval@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _SYSTEM_SYSCALL_CLOCK_INFO_H +#define _SYSTEM_SYSCALL_CLOCK_INFO_H + + +enum which_process_info { + TEAM_ID = 1, + THREAD_ID, +}; + + +#endif /* _SYSTEM_SYSCALL_CLOCK_INFO_H */ diff --git a/headers/private/system/syscalls.h b/headers/private/system/syscalls.h index bb488bb0f3..6501316cc8 100644 --- a/headers/private/system/syscalls.h +++ b/headers/private/system/syscalls.h @@ -412,6 +412,7 @@ extern status_t _kern_get_real_time_clock_is_gmt(bool *_isGMT); extern status_t _kern_get_clock(clockid_t clockID, bigtime_t* _time); extern status_t _kern_set_clock(clockid_t clockID, bigtime_t time); +extern status_t _kern_get_cpuclockid(thread_id id, int32 which, clockid_t* _clockID); extern bigtime_t _kern_system_time(); extern status_t _kern_snooze_etc(bigtime_t time, int timebase, int32 flags, diff --git a/src/system/kernel/UserTimer.cpp b/src/system/kernel/UserTimer.cpp index 2513480f45..50b458ee2a 100644 --- a/src/system/kernel/UserTimer.cpp +++ b/src/system/kernel/UserTimer.cpp @@ -14,12 +14,19 @@ #include #include #include +#include #include #include #include #include +#define CPUCLOCK_TEAM 0x00000000 +#define CPUCLOCK_THREAD 0x80000000 +#define CPUCLOCK_SPECIAL 0xc0000000 +#define CPUCLOCK_ID_MASK (~(CPUCLOCK_SPECIAL)) + + // Minimum interval length in microseconds for a periodic timer. This is not a // restriction on the user timer interval length itself, but the minimum time // span by which we advance the start time for kernel timers. A shorted user @@ -1505,13 +1512,24 @@ user_timer_get_clock(clockid_t clockID, bigtime_t& _time) team_id teamID; if (clockID == CLOCK_PROCESS_CPUTIME_ID) { teamID = B_CURRENT_TEAM; - } else { - if (clockID < 0) + } else if ((clockID & CPUCLOCK_THREAD) == CPUCLOCK_THREAD) { + thread_id threadID = clockID & CPUCLOCK_ID_MASK; + // get the thread + Thread* thread = Thread::Get(threadID); + if (thread == NULL) return B_BAD_VALUE; - if (clockID == team_get_kernel_team_id()) + BReference threadReference(thread, true); + if (thread->team == team_get_kernel_team()) return B_NOT_ALLOWED; + // get the time + InterruptsSpinLocker timeLocker(thread->time_lock); + _time = thread->CPUTime(false); - teamID = clockID; + return B_OK; + } else if ((clockID & CPUCLOCK_TEAM) == CPUCLOCK_TEAM) { + teamID = clockID & CPUCLOCK_ID_MASK; + if (teamID == team_get_kernel_team_id()) + return B_NOT_ALLOWED; } // get the team @@ -1657,13 +1675,31 @@ _user_set_clock(clockid_t clockID, bigtime_t time) team_id teamID; if (clockID == CLOCK_PROCESS_CPUTIME_ID) { teamID = B_CURRENT_TEAM; - } else { - if (clockID < 0) + } else if ((clockID & CPUCLOCK_THREAD) != 0) { + thread_id threadID = clockID & CPUCLOCK_ID_MASK; + if (threadID < 0) return B_BAD_VALUE; - if (clockID == team_get_kernel_team_id()) + // get the thread + Thread* thread = Thread::Get(threadID); + if (thread == NULL) + return B_BAD_VALUE; + BReference threadReference(thread, true); + if (thread->team == team_get_kernel_team()) return B_NOT_ALLOWED; - teamID = clockID; + // set the time offset + InterruptsSpinLocker timeLocker(thread->time_lock); + bigtime_t diff = time - thread->CPUTime(false); + thread->cpu_clock_offset += diff; + + thread_clock_changed(thread, diff); + return B_OK; + } else { + teamID = clockID & CPUCLOCK_ID_MASK; + if (teamID < 0) + return B_BAD_VALUE; + if (teamID == team_get_kernel_team_id()) + return B_NOT_ALLOWED; } // get the team @@ -1686,6 +1722,34 @@ _user_set_clock(clockid_t clockID, bigtime_t time) } +status_t +_user_get_cpuclockid(thread_id id, int32 which, clockid_t* userclockID) +{ + clockid_t clockID; + if (which != TEAM_ID && which != THREAD_ID) + return B_BAD_VALUE; + + if (which == TEAM_ID) { + Team* team = Team::Get(id); + if (team == NULL) + return B_BAD_VALUE; + clockID = id | CPUCLOCK_TEAM; + } else if (which == THREAD_ID) { + Thread* thread = Thread::Get(id); + if (thread == NULL) + return B_BAD_VALUE; + clockID = id | CPUCLOCK_THREAD; + } + + if (userclockID != NULL + && (!IS_USER_ADDRESS(userclockID) + || user_memcpy(userclockID, &clockID, sizeof(clockID)) != B_OK)) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + int32 _user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags, const struct sigevent* userEvent, diff --git a/src/system/libroot/posix/time/clock_support.cpp b/src/system/libroot/posix/time/clock_support.cpp index 2a9712021e..baedab9046 100644 --- a/src/system/libroot/posix/time/clock_support.cpp +++ b/src/system/libroot/posix/time/clock_support.cpp @@ -14,8 +14,10 @@ #include #include -#include +#include +#include #include +#include #include @@ -145,9 +147,7 @@ clock_getcpuclockid(pid_t pid, clockid_t* _clockID) return 0; } - // test-get the time to verify the team exists and we have permission - bigtime_t microSeconds; - status_t error = _kern_get_clock(pid, µSeconds); + status_t error = _kern_get_cpuclockid(pid, TEAM_ID, _clockID); if (error != B_OK) { // Since pid is > 0, B_BAD_VALUE always means a team with that ID // doesn't exist. Translate the error code accordingly. @@ -156,6 +156,24 @@ clock_getcpuclockid(pid_t pid, clockid_t* _clockID) return error; } - *_clockID = pid; + return 0; +} + + +int +pthread_getcpuclockid(pthread_t thread, clockid_t* _clockID) +{ + if (thread->id < 0) + return ESRCH; + + status_t error = _kern_get_cpuclockid(thread->id, THREAD_ID, _clockID); + if (error != B_OK) { + // Since thread->id is > 0, B_BAD_VALUE always means a thread with that ID + // doesn't exist. Translate the error code accordingly. + if (error == B_BAD_VALUE) + return ESRCH; + return error; + } + return 0; } diff --git a/src/tests/system/libroot/posix/Jamfile b/src/tests/system/libroot/posix/Jamfile index d858f43f32..43beb17b71 100644 --- a/src/tests/system/libroot/posix/Jamfile +++ b/src/tests/system/libroot/posix/Jamfile @@ -43,6 +43,7 @@ SimpleTest truncate : truncate.cpp ; SimpleTest init_rld_after_fork_test : init_rld_after_fork_test.cpp ; SimpleTest user_thread_fork_test : user_thread_fork_test.cpp ; SimpleTest pthread_barrier_test : pthread_barrier_test.cpp ; +SimpleTest pthread_clock_test : pthread_clock_test.cpp ; SimpleTest posix_spawn_test : posix_spawn_test.cpp ; SimpleTest posix_spawn_redir_test : posix_spawn_redir_test.c ; SimpleTest posix_spawn_redir_err : posix_spawn_redir_err.c ; diff --git a/src/tests/system/libroot/posix/pthread_clock_test.cpp b/src/tests/system/libroot/posix/pthread_clock_test.cpp new file mode 100644 index 0000000000..0fb7f32a50 --- /dev/null +++ b/src/tests/system/libroot/posix/pthread_clock_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2024, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include + +void* +threadFn(void* ptr) +{ + snooze(1000000); + return NULL; +} + + +int +main() +{ + pthread_t t; + pthread_create(&t, NULL, threadFn, NULL); + + clockid_t c; + if (pthread_getcpuclockid(t, &c) != 0) + return 1; + timespec ts; + if (clock_gettime(c, &ts) != 0) + return 1; + + if (clock_getcpuclockid(getpid(), &c) != 0) + return 1; + if (clock_gettime(c, &ts) != 0) + return 1; + + if (pthread_join(t, NULL) != 0) + return 1; + return 0; +}