POSIX: add pthread_getcpuclockid()

fix #15615

Change-Id: I6b34f97464fac97d0b339fa338cfc5df34536080
Reviewed-on: https://review.haiku-os.org/c/haiku/+/8681
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
Jérôme Duval 2024-12-13 17:15:55 +01:00
parent 1ad6193d82
commit 04c90835ac
8 changed files with 153 additions and 13 deletions

View File

@ -196,6 +196,7 @@ extern int pthread_atfork(void (*prepare)(void), void (*parent)(void),
void (*child)(void)); void (*child)(void));
extern int pthread_once(pthread_once_t *once_control, extern int pthread_once(pthread_once_t *once_control,
void (*init_routine)(void)); void (*init_routine)(void));
extern int pthread_getcpuclockid(pthread_t thread_id, clockid_t* clock_id);
/* thread attributes functions */ /* thread attributes functions */
extern int pthread_attr_destroy(pthread_attr_t *attr); extern int pthread_attr_destroy(pthread_attr_t *attr);

View File

@ -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_get_clock(clockid_t clockID, bigtime_t* _time);
status_t _user_set_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, int32 _user_create_timer(clockid_t clockID, thread_id threadID,
uint32 flags, const struct sigevent* event, uint32 flags, const struct sigevent* event,

View File

@ -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 */

View File

@ -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_get_clock(clockid_t clockID, bigtime_t* _time);
extern status_t _kern_set_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 bigtime_t _kern_system_time();
extern status_t _kern_snooze_etc(bigtime_t time, int timebase, int32 flags, extern status_t _kern_snooze_etc(bigtime_t time, int timebase, int32 flags,

View File

@ -14,12 +14,19 @@
#include <debug.h> #include <debug.h>
#include <kernel.h> #include <kernel.h>
#include <real_time_clock.h> #include <real_time_clock.h>
#include <syscall_clock_info.h>
#include <team.h> #include <team.h>
#include <thread_types.h> #include <thread_types.h>
#include <UserEvent.h> #include <UserEvent.h>
#include <util/AutoLock.h> #include <util/AutoLock.h>
#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 // 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 // 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 // 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; team_id teamID;
if (clockID == CLOCK_PROCESS_CPUTIME_ID) { if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
teamID = B_CURRENT_TEAM; teamID = B_CURRENT_TEAM;
} else { } else if ((clockID & CPUCLOCK_THREAD) == CPUCLOCK_THREAD) {
if (clockID < 0) thread_id threadID = clockID & CPUCLOCK_ID_MASK;
// get the thread
Thread* thread = Thread::Get(threadID);
if (thread == NULL)
return B_BAD_VALUE; return B_BAD_VALUE;
if (clockID == team_get_kernel_team_id()) BReference<Thread> threadReference(thread, true);
if (thread->team == team_get_kernel_team())
return B_NOT_ALLOWED; 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 // get the team
@ -1657,13 +1675,31 @@ _user_set_clock(clockid_t clockID, bigtime_t time)
team_id teamID; team_id teamID;
if (clockID == CLOCK_PROCESS_CPUTIME_ID) { if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
teamID = B_CURRENT_TEAM; teamID = B_CURRENT_TEAM;
} else { } else if ((clockID & CPUCLOCK_THREAD) != 0) {
if (clockID < 0) thread_id threadID = clockID & CPUCLOCK_ID_MASK;
if (threadID < 0)
return B_BAD_VALUE; 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<Thread> threadReference(thread, true);
if (thread->team == team_get_kernel_team())
return B_NOT_ALLOWED; 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 // 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 int32
_user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags, _user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags,
const struct sigevent* userEvent, const struct sigevent* userEvent,

View File

@ -14,8 +14,10 @@
#include <OS.h> #include <OS.h>
#include <errno_private.h> #include <errno_private.h>
#include <time_private.h> #include <pthread_private.h>
#include <syscall_clock_info.h>
#include <syscall_utils.h> #include <syscall_utils.h>
#include <time_private.h>
#include <syscalls.h> #include <syscalls.h>
@ -145,9 +147,7 @@ clock_getcpuclockid(pid_t pid, clockid_t* _clockID)
return 0; return 0;
} }
// test-get the time to verify the team exists and we have permission status_t error = _kern_get_cpuclockid(pid, TEAM_ID, _clockID);
bigtime_t microSeconds;
status_t error = _kern_get_clock(pid, &microSeconds);
if (error != B_OK) { if (error != B_OK) {
// Since pid is > 0, B_BAD_VALUE always means a team with that ID // Since pid is > 0, B_BAD_VALUE always means a team with that ID
// doesn't exist. Translate the error code accordingly. // doesn't exist. Translate the error code accordingly.
@ -156,6 +156,24 @@ clock_getcpuclockid(pid_t pid, clockid_t* _clockID)
return error; 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; return 0;
} }

View File

@ -43,6 +43,7 @@ SimpleTest <test>truncate : truncate.cpp ;
SimpleTest init_rld_after_fork_test : init_rld_after_fork_test.cpp ; SimpleTest init_rld_after_fork_test : init_rld_after_fork_test.cpp ;
SimpleTest user_thread_fork_test : user_thread_fork_test.cpp ; SimpleTest user_thread_fork_test : user_thread_fork_test.cpp ;
SimpleTest pthread_barrier_test : pthread_barrier_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_test : posix_spawn_test.cpp ;
SimpleTest posix_spawn_redir_test : posix_spawn_redir_test.c ; SimpleTest posix_spawn_redir_test : posix_spawn_redir_test.c ;
SimpleTest posix_spawn_redir_err : posix_spawn_redir_err.c ; SimpleTest posix_spawn_redir_err : posix_spawn_redir_err.c ;

View File

@ -0,0 +1,39 @@
/*
* Copyright 2024, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <pthread.h>
#include <OS.h>
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;
}