Posix compliant CLOCK_THREAD_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID The following patch makes glibc not provide the above clocks and use the kernel clocks instead if either of the following condition is met: 1. __ASSUME_POSIX_TIMERS is set 2. A call to probe the posix function is made if the corresponding __NR_clock_* is defined. If the call is successful then the kernel clocks will be used. Otherwise glibc will fall back to its own implementation of the clocks. Signed-off-by: Christoph Lameter Index: libc/sysdeps/unix/sysv/linux/clock_getres.c =================================================================== --- libc.orig/sysdeps/unix/sysv/linux/clock_getres.c 2004-09-28 15:22:02.351950224 -0700 +++ libc/sysdeps/unix/sysv/linux/clock_getres.c 2004-09-30 10:29:36.220774632 -0700 @@ -22,19 +22,24 @@ #ifdef __ASSUME_POSIX_TIMERS -/* This means the REALTIME and MONOTONIC clock are definitely - supported in the kernel. */ +/* This means the all clocks are definitely supported in the kernel. */ # define SYSDEP_GETRES \ case CLOCK_REALTIME: \ case CLOCK_MONOTONIC: \ + case CLOCK_THREAD_CPUTIME_ID: \ + case CLOCK_PROCESS_CPUTIME_ID: \ retval = INLINE_SYSCALL (clock_getres, 2, clock_id, res); \ break #elif defined __NR_clock_getres /* Is the syscall known to exist? */ extern int __libc_missing_posix_timers attribute_hidden; +extern int __libc_missing_posix_cputimers attribute_hidden; /* The REALTIME and MONOTONIC clock might be available. Try the - syscall first. */ + syscall first. Also in some version of Linux 2.6.x + CLOCK_PROCESS/THREAD_CPUTIME_ID may be available. Try those + syscalls first before falling back to the use of HP_TIMING. + */ # define SYSDEP_GETRES \ case CLOCK_REALTIME: \ case CLOCK_MONOTONIC: \ @@ -65,6 +70,33 @@ else \ __set_errno (e); \ } \ + break; \ + case CLOCK_THREAD_CPUTIME_ID: \ + case CLOCK_PROCESS_CPUTIME_ID: \ + { \ + int e = EINVAL; \ + \ + if (!__libc_missing_posix_cputimers) \ + { \ + INTERNAL_SYSCALL_DECL (err); \ + int r = INTERNAL_SYSCALL (clock_getres, err, 2, clock_id, res); \ + if (!INTERNAL_SYSCALL_ERROR_P (r, err)) \ + { \ + retval = 0; \ + break; \ + } \ + \ + e = INTERNAL_SYSCALL_ERRNO (r, err); \ + if (e == ENOSYS) \ + { \ + __libc_missing_posix_cputimers = 1; \ + e = EINVAL; \ + } \ + } \ + \ + /* Fallback code. */ \ + return clock_getres_thread_process(res); \ + } \ break #endif Index: libc/sysdeps/unix/clock_gettime.c =================================================================== --- libc.orig/sysdeps/unix/clock_gettime.c 2004-09-28 15:22:02.192974392 -0700 +++ libc/sysdeps/unix/clock_gettime.c 2004-09-30 10:58:21.467497264 -0700 @@ -35,6 +35,54 @@ extern int __pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq, struct timespec *tp) __attribute__ ((__weak__)); + +int clock_process(struct timespec *tp) { + hp_timing_t tsc; + + if (__builtin_expect (freq == 0, 0)) + { + /* This can only happen if we haven't initialized the `freq' + variable yet. Do this now. We don't have to protect this + code against multiple execution since all of them should + lead to the same result. */ + freq = __get_clockfreq (); + if (__builtin_expect (freq == 0, 0)) + /* Something went wrong. */ + return -1; + } + + /* Get the current counter. */ + HP_TIMING_NOW (tsc); + + /* Compute the offset since the start time of the process. */ + tsc -= GL(dl_cpuclock_offset); + + /* Compute the seconds. */ + tp->tv_sec = tsc / freq; + + /* And the nanoseconds. This computation should be stable until + we get machines with about 16GHz frequency. */ + tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq; + + return 0; + } + +int clock_thread(struct timespec *tp) { + + if (__builtin_expect (freq == 0, 0)) + { + /* This can only happen if we haven't initialized the `freq' + variable yet. Do this now. We don't have to protect this + code against multiple execution since all of them should + lead to the same result. */ + freq = __get_clockfreq (); + if (__builtin_expect (freq == 0, 0)) + return -1; + } + + return __pthread_clock_gettime (CLOCK_THREAD_CPUTIME_ID, freq, tp); + } + #endif @@ -67,55 +115,21 @@ default: #if HP_TIMING_AVAIL - if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) - != CLOCK_THREAD_CPUTIME_ID) + if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) + != CLOCK_THREAD_CPUTIME_ID) #endif + { __set_errno (EINVAL); break; } #if HP_TIMING_AVAIL - /* FALLTHROUGH. */ - case CLOCK_PROCESS_CPUTIME_ID: - { - hp_timing_t tsc; - - if (__builtin_expect (freq == 0, 0)) - { - /* This can only happen if we haven't initialized the `freq' - variable yet. Do this now. We don't have to protect this - code against multiple execution since all of them should - lead to the same result. */ - freq = __get_clockfreq (); - if (__builtin_expect (freq == 0, 0)) - /* Something went wrong. */ - break; - } - - if (clock_id != CLOCK_PROCESS_CPUTIME_ID - && __pthread_clock_gettime != NULL) - { - retval = __pthread_clock_gettime (clock_id, freq, tp); - break; - } - - /* Get the current counter. */ - HP_TIMING_NOW (tsc); - - /* Compute the offset since the start time of the process. */ - tsc -= GL(dl_cpuclock_offset); - - /* Compute the seconds. */ - tp->tv_sec = tsc / freq; - - /* And the nanoseconds. This computation should be stable until - we get machines with about 16GHz frequency. */ - tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq; - - retval = 0; - } - break; + /* FALLTHROUGH. */ + if (clock_id == CLOCK_PROCESS_CPUTIME_ID) + return clock_process(tp); + else + return clock_thread(tp); #endif } Index: libc/sysdeps/unix/sysv/linux/clock_settime.c =================================================================== --- libc.orig/sysdeps/unix/sysv/linux/clock_settime.c 2004-09-28 15:22:02.365948096 -0700 +++ libc/sysdeps/unix/sysv/linux/clock_settime.c 2004-09-30 11:56:57.390995648 -0700 @@ -31,6 +31,7 @@ #elif defined __NR_clock_settime /* Is the syscall known to exist? */ extern int __libc_missing_posix_timers attribute_hidden; +extern int __libc_missing_posix_cputimers attribute_hidden; /* The REALTIME clock might be available. Try the syscall first. */ # define SYSDEP_SETTIME \ @@ -65,6 +66,36 @@ retval = -1; \ } \ } \ + break; \ + case CLOCK_THREAD_CPUTIME_ID: \ + case CLOCK_PROCESS_CPUTIME_ID: \ + { \ + int e = EINVAL; \ + \ + if (!__libc_missing_posix_cputimers) \ + { \ + INTERNAL_SYSCALL_DECL (err); \ + int r = INTERNAL_SYSCALL (clock_settime, err, 2, clock_id, tp); \ + if (!INTERNAL_SYSCALL_ERROR_P (r, err)) \ + { \ + retval = 0; \ + break; \ + } \ + \ + e = INTERNAL_SYSCALL_ERRNO (r, err); \ + if (e == ENOSYS) \ + { \ + __libc_missing_posix_cputimers = 1; \ + e = EINVAL; \ + } \ + } \ + \ + /* Fallback code. */ \ + if (clock_id == CLOCK_PROCESS_CPUTIME_ID) \ + return clock_settime_process(tp); \ + else \ + return clock_settime_process(tp); \ + } \ break #endif Index: libc/sysdeps/posix/clock_getres.c =================================================================== --- libc.orig/sysdeps/posix/clock_getres.c 2004-09-28 15:22:02.032998712 -0700 +++ libc/sysdeps/posix/clock_getres.c 2004-09-30 10:37:30.943605792 -0700 @@ -27,8 +27,32 @@ #if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME /* Clock frequency of the processor. */ static long int nsec; -#endif + +int clock_getres_thread_process(struct timespec *res) { + if (__builtin_expect (nsec == 0, 0)) + { + hp_timing_t freq; + /* This can only happen if we haven't initialized the `freq' + variable yet. Do this now. We don't have to protect this + code against multiple execution since all of them should + lead to the same result. */ + freq = __get_clockfreq (); + if (__builtin_expect (freq == 0, 0)) + /* Something went wrong. */ + return -EINVAL; + + nsec = MAX (UINT64_C (1000000000) / freq, 1); + } + + /* File in the values. The seconds are always zero (unless we + have a 1Hz machine). */ + res->tv_sec = 0; + res->tv_nsec = nsec; + + return 0; +} +#endif /* Get resolution of clock. */ int @@ -74,35 +98,7 @@ break; } -#if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME - /* FALLTHROUGH. */ - case CLOCK_PROCESS_CPUTIME_ID: - { - if (__builtin_expect (nsec == 0, 0)) - { - hp_timing_t freq; - - /* This can only happen if we haven't initialized the `freq' - variable yet. Do this now. We don't have to protect this - code against multiple execution since all of them should - lead to the same result. */ - freq = __get_clockfreq (); - if (__builtin_expect (freq == 0, 0)) - /* Something went wrong. */ - break; - - nsec = MAX (UINT64_C (1000000000) / freq, 1); - } - - /* File in the values. The seconds are always zero (unless we - have a 1Hz machine). */ - res->tv_sec = 0; - res->tv_nsec = nsec; - - retval = 0; - } - break; -#endif + return clock_getres_thread_process(res); } return retval; Index: libc/sysdeps/unix/clock_settime.c =================================================================== --- libc.orig/sysdeps/unix/clock_settime.c 2004-02-19 01:00:43.000000000 -0800 +++ libc/sysdeps/unix/clock_settime.c 2004-09-30 11:32:30.911934352 -0700 @@ -33,6 +33,60 @@ /* This function is defined in the thread library. */ extern void __pthread_clock_settime (clockid_t clock_id, hp_timing_t offset) __attribute__ ((__weak__)); + +int clock_settime_process(const struct timespec *tp) { + hp_timing_t tsc; + hp_timing_t usertime; + + /* First thing is to get the current time. */ + HP_TIMING_NOW (tsc); + + if (__builtin_expect (freq == 0, 0)) + { + /* This can only happen if we haven't initialized the `freq' + variable yet. Do this now. We don't have to protect this + code against multiple execution since all of them should + lead to the same result. */ + freq = __get_clockfreq (); + if (__builtin_expect (freq == 0, 0)) + return -1; + } + + /* Convert the user-provided time into CPU ticks. */ + usertime = tp->tv_sec * freq + (tp->tv_nsec * freq) / 1000000000ull; + + /* Determine the offset and use it as the new base value. */ + GL(dl_cpuclock_offset) = tsc - usertime; + return 0; +} + +int clock_settime_thread(const struct timespec *tp) { + hp_timing_t tsc; + hp_timing_t usertime; + + /* First thing is to get the current time. */ + HP_TIMING_NOW (tsc); + + if (__builtin_expect (freq == 0, 0)) + { + /* This can only happen if we haven't initialized the `freq' + variable yet. Do this now. We don't have to protect this + code against multiple execution since all of them should + lead to the same result. */ + freq = __get_clockfreq (); + if (__builtin_expect (freq == 0, 0)) + /* Something went wrong. */ + return -1; + } + + /* Convert the user-provided time into CPU ticks. */ + usertime = tp->tv_sec * freq + (tp->tv_nsec * freq) / 1000000000ull; + + /* Determine the offset and use it as the new base value. */ + __pthread_clock_settime (CLOCK_THREAD_CPUTIME_ID, tsc - usertime); + + return 0; +} #endif @@ -82,42 +136,10 @@ #if HP_TIMING_AVAIL /* FALLTHROUGH. */ - case CLOCK_PROCESS_CPUTIME_ID: - { - hp_timing_t tsc; - hp_timing_t usertime; - - /* First thing is to get the current time. */ - HP_TIMING_NOW (tsc); - - if (__builtin_expect (freq == 0, 0)) - { - /* This can only happen if we haven't initialized the `freq' - variable yet. Do this now. We don't have to protect this - code against multiple execution since all of them should - lead to the same result. */ - freq = __get_clockfreq (); - if (__builtin_expect (freq == 0, 0)) - { - /* Something went wrong. */ - retval = -1; - break; - } - } - - /* Convert the user-provided time into CPU ticks. */ - usertime = tp->tv_sec * freq + (tp->tv_nsec * freq) / 1000000000ull; - - /* Determine the offset and use it as the new base value. */ - if (clock_id == CLOCK_PROCESS_CPUTIME_ID - || __pthread_clock_settime == NULL) - GL(dl_cpuclock_offset) = tsc - usertime; - else - __pthread_clock_settime (clock_id, tsc - usertime); - - retval = 0; - } - break; + if (clock_id == CLOCK_PROCESS_CPUTIME_ID) + return clock_settime_process(tp); + else + return clock_settime_thread(tp); #endif } Index: libc/sysdeps/unix/sysv/linux/clock_gettime.c =================================================================== --- libc.orig/sysdeps/unix/sysv/linux/clock_gettime.c 2004-09-28 15:22:02.359949008 -0700 +++ libc/sysdeps/unix/sysv/linux/clock_gettime.c 2004-09-30 10:14:51.635252072 -0700 @@ -22,19 +22,23 @@ #ifdef __ASSUME_POSIX_TIMERS -/* This means the REALTIME and MONOTONIC clock are definitely - supported in the kernel. */ +/* This means that all clocks are definitely supported in the kernel. */ # define SYSDEP_GETTIME \ case CLOCK_REALTIME: \ case CLOCK_MONOTONIC: \ + case CLOCK_THREAD_CPUTIME_ID: \ + case CLOCK_PROCESS_CPUTIME_ID: \ retval = INLINE_SYSCALL (clock_gettime, 2, clock_id, tp); \ break #elif defined __NR_clock_gettime /* Is the syscall known to exist? */ int __libc_missing_posix_timers attribute_hidden; +int __libc_missing_posix_cputimers attribute_hidden; /* The REALTIME and MONOTONIC clock might be available. Try the - syscall first. */ + syscall first. + Do a similar thing for THREAD_CPUTIME_ID and PROCESS_CPUTIME_ID + */ # define SYSDEP_GETTIME \ case CLOCK_REALTIME: \ case CLOCK_MONOTONIC: \ @@ -65,6 +69,34 @@ else \ __set_errno (e); \ } \ + break; \ + case CLOCK_PROCESS_CPUTIME_ID: \ + case CLOCK_THREAD_CPUTIME_ID: \ + { \ + int e = EINVAL; \ + \ + if (!__libc_missing_posix_cputimers) \ + { \ + INTERNAL_SYSCALL_DECL (err); \ + int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp); \ + if (!INTERNAL_SYSCALL_ERROR_P (r, err)) \ + { \ + retval = 0; \ + break; \ + } \ + \ + e = INTERNAL_SYSCALL_ERRNO (r, err); \ + if (e == ENOSYS) \ + { \ + __libc_missing_posix_cputimers = 1; \ + e = EINVAL; \ + } \ + } \ + \ + /* Fallback code. */ \ + if (clock_id == CLOCK_PROCESS_CPUTIME_ID) return clock_process(tp); \ + else return clock_thread(tp); \ + } \ break #endif