mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-09 11:58:23 +08:00
Compare commits
54
Commits
@@ -134,6 +134,7 @@ enum __rusage_who
|
||||
};
|
||||
|
||||
#include <bits/types/struct_timeval.h>
|
||||
#include <bits/types/struct_timeval64.h>
|
||||
#include <bits/types/struct_rusage.h>
|
||||
|
||||
/* Priority limits. */
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/* bits/time64.h -- underlying types for __time64_t. Generic version.
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _BITS_TYPES_H
|
||||
# error "Never include <bits/time64.h> directly; use <sys/types.h> instead."
|
||||
#endif
|
||||
|
||||
#ifndef _BITS_TIME64_H
|
||||
#define _BITS_TIME64_H 1
|
||||
|
||||
/* Define __TIME64_T_TYPE so that it is always a 64-bit type. */
|
||||
|
||||
#if __TIMESIZE == 64
|
||||
/* If we already have 64-bit time type then use it. */
|
||||
# define __TIME64_T_TYPE __TIME_T_TYPE
|
||||
#else
|
||||
/* Define a 64-bit time type alongsize the 32-bit one. */
|
||||
# define __TIME64_T_TYPE __SQUAD_TYPE
|
||||
#endif
|
||||
|
||||
#endif /* bits/time64.h */
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef __timespec64_defined
|
||||
#define __timespec64_defined 1
|
||||
|
||||
#include <bits/types.h>
|
||||
#include <bits/timesize.h>
|
||||
#include <endian.h>
|
||||
|
||||
/* The glibc-internal Y2038-proof structure for a time value.
|
||||
To keep things Posix-ish, we keep the nanoseconds field a 32-bit
|
||||
signed long, but since the Linux field is a 64-bit signed int, we
|
||||
pad our tv_nsec with a 32-bit int, which should always be 0.
|
||||
Note that this is the glibc-internal type; in the public type, the
|
||||
padding is an anonymous 32-bit bitfield, so that source-code initializers
|
||||
keep working. */
|
||||
#if __TIMESIZE==64
|
||||
# define __timespec64 timespec
|
||||
#else
|
||||
#define TIMSPEC64_TV_PAD_DEFINED
|
||||
# if BYTE_ORDER == BIG_ENDIAN
|
||||
struct __timespec64
|
||||
{
|
||||
__time64_t tv_sec; /* Seconds */
|
||||
int tv_pad: 32; /* Padding named for checking/setting */
|
||||
__syscall_slong_t tv_nsec; /* Nanoseconds */
|
||||
};
|
||||
# else
|
||||
struct __timespec64
|
||||
{
|
||||
__time64_t tv_sec; /* Seconds */
|
||||
__syscall_slong_t tv_nsec; /* Nanoseconds */
|
||||
int tv_pad: 32; /* Padding named for checking/setting */
|
||||
};
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
#include <time/bits/types/struct_itimerspec64.h>
|
||||
@@ -0,0 +1 @@
|
||||
#include <time/bits/types/struct_timeval64.h>
|
||||
@@ -364,6 +364,25 @@
|
||||
# define __USE_FILE_OFFSET64 1
|
||||
#endif
|
||||
|
||||
/* we need to know the word size in order to check the time size */
|
||||
#include <bits/wordsize.h>
|
||||
|
||||
#if defined _TIME_BITS
|
||||
# if _TIME_BITS == 64
|
||||
# if ! defined(_FILE_OFFSET_BITS) || _FILE_OFFSET_BITS != 64
|
||||
# error _TIME_BIT==64 is allowed only when _FILE_OFFSET_BITS==64
|
||||
# elif __WORDSIZE == 32
|
||||
# define __USE_TIME_BITS64 1
|
||||
# endif
|
||||
# elif __TIME_BITS == 32
|
||||
# if __WORDSIZE > 32
|
||||
# error __TIME_BITS=32 is not compatible with __WORDSIZE > 32
|
||||
# endif
|
||||
# else
|
||||
# error Invalid _TIME_BITS value (can only be 32 or 64)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined _DEFAULT_SOURCE
|
||||
# define __USE_MISC 1
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#ifndef _ISOMAC
|
||||
/* Now define the internal interfaces. */
|
||||
|
||||
extern int __pselect (int __nfds, fd_set *__readfds,
|
||||
fd_set *__writefds, fd_set *__exceptfds,
|
||||
const struct timespec *__timeout,
|
||||
@@ -14,5 +15,17 @@ extern int __select (int __nfds, fd_set *__restrict __readfds,
|
||||
struct timeval *__restrict __timeout);
|
||||
libc_hidden_proto (__select)
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
extern int __pselect64 (int __nfds, fd_set *__readfds,
|
||||
fd_set *__writefds, fd_set *__exceptfds,
|
||||
const struct __timespec64 *__timeout,
|
||||
const __sigset_t *__sigmask);
|
||||
|
||||
extern int __select64 (int __nfds, fd_set *__restrict __readfds,
|
||||
fd_set *__restrict __writefds,
|
||||
fd_set *__restrict __exceptfds,
|
||||
struct __timeval64 *__restrict __timeout);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,28 @@
|
||||
#include <io/sys/stat.h>
|
||||
|
||||
#ifndef _ISOMAC
|
||||
|
||||
/* Used for 64-bit time implementations */
|
||||
struct __stat64_t64
|
||||
{
|
||||
__dev_t st_dev; /* Device. */
|
||||
|
||||
__ino_t __st_ino; /* 32bit file serial number. */
|
||||
__mode_t st_mode; /* File mode. */
|
||||
__nlink_t st_nlink; /* Link count. */
|
||||
__uid_t st_uid; /* User ID of the file's owner. */
|
||||
__gid_t st_gid; /* Group ID of the file's group.*/
|
||||
__dev_t st_rdev; /* Device number, if device. */
|
||||
__off64_t st_size; /* Size of file, in bytes. */
|
||||
__blksize_t st_blksize; /* Optimal block size for I/O. */
|
||||
|
||||
__blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */
|
||||
struct __timespec64 st_atim; /* Time of last access. */
|
||||
struct __timespec64 st_mtim; /* Time of last modification. */
|
||||
struct __timespec64 st_ctim; /* Time of last status change. */
|
||||
__ino64_t st_ino; /* File serial number. */
|
||||
};
|
||||
|
||||
/* Now define the internal interfaces. */
|
||||
extern int __stat (const char *__file, struct stat *__buf);
|
||||
extern int __fstat (int __fd, struct stat *__buf);
|
||||
@@ -14,6 +36,17 @@ extern int __mkdir (const char *__path, __mode_t __mode);
|
||||
libc_hidden_proto (__mkdir)
|
||||
extern int __mknod (const char *__path,
|
||||
__mode_t __mode, __dev_t __dev);
|
||||
extern int __fxstat64_time64 (int __ver, int __fildes,
|
||||
struct __stat64_t64 *__stat_buf);
|
||||
extern int __xstat64_time64 (int __ver, const char *__filename,
|
||||
struct __stat64_t64 *__stat_buf);
|
||||
extern int __lxstat64_time64 (int __ver, const char *__filename,
|
||||
struct __stat64_t64 *__stat_buf);
|
||||
extern int __fxstatat64_time64 (int __ver, int __fildes,
|
||||
const char *__filename,
|
||||
struct __stat64_t64 *__stat_buf,
|
||||
int __flag);
|
||||
|
||||
#if IS_IN (libc) || (IS_IN (rtld) && !defined NO_RTLD_HIDDEN)
|
||||
hidden_proto (__fxstat)
|
||||
hidden_proto (__fxstat64)
|
||||
|
||||
@@ -24,9 +24,13 @@ extern int __gettimeofday (struct timeval *__tv,
|
||||
struct timezone *__tz);
|
||||
libc_hidden_proto (__gettimeofday)
|
||||
libc_hidden_proto (gettimeofday)
|
||||
extern int __gettimeofday64 (struct __timeval64 *__tv,
|
||||
struct timezone *__tz);
|
||||
extern int __settimeofday (const struct timeval *__tv,
|
||||
const struct timezone *__tz)
|
||||
attribute_hidden;
|
||||
extern int __settimeofday64 (const struct __timeval64 *__tv,
|
||||
const struct timezone *__tz);
|
||||
extern int __adjtime (const struct timeval *__delta,
|
||||
struct timeval *__olddelta);
|
||||
extern int __getitimer (enum __itimer_which __which,
|
||||
|
||||
+206
-16
@@ -3,6 +3,9 @@
|
||||
|
||||
#ifndef _ISOMAC
|
||||
# include <bits/types/locale_t.h>
|
||||
# include <bits/types/struct___timespec64.h>
|
||||
# include <bits/types/struct___timeval64.h>
|
||||
# include <stdbool.h>
|
||||
|
||||
extern __typeof (strftime_l) __strftime_l;
|
||||
libc_hidden_proto (__strftime_l)
|
||||
@@ -16,6 +19,51 @@ libc_hidden_proto (localtime)
|
||||
libc_hidden_proto (strftime)
|
||||
libc_hidden_proto (strptime)
|
||||
|
||||
#if __TIMESIZE == 64
|
||||
# define __timegm64 timegm
|
||||
# define __mktime64 mktime
|
||||
# define __timelocal64 timelocal
|
||||
#else
|
||||
extern __time64_t __timegm64 (struct tm *__tp) __THROW;
|
||||
extern __time64_t __mktime64 (struct tm *__tp) __THROW;
|
||||
/* Another name for `__mktime64'. */
|
||||
extern __time64_t __timelocal64 (struct tm *__tp) __THROW;
|
||||
|
||||
libc_hidden_proto (__mktime64)
|
||||
libc_hidden_proto (__timelocal64)
|
||||
#endif
|
||||
|
||||
/* 64-bit time version of the current struct timex */
|
||||
struct __timex64
|
||||
{
|
||||
unsigned int modes; /* mode selector */
|
||||
__syscall_slong_t offset; /* time offset (usec) */
|
||||
__syscall_slong_t freq; /* frequency offset (scaled ppm) */
|
||||
__syscall_slong_t maxerror; /* maximum error (usec) */
|
||||
__syscall_slong_t esterror; /* estimated error (usec) */
|
||||
int status; /* clock command/status */
|
||||
__syscall_slong_t constant; /* pll time constant */
|
||||
__syscall_slong_t precision; /* clock precision (usec) (ro) */
|
||||
__syscall_slong_t tolerance; /* clock frequency tolerance (ppm) (ro) */
|
||||
struct __timeval64 time; /* (read only, except for ADJ_SETOFFSET) */
|
||||
__syscall_slong_t tick; /* (modified) usecs between clock ticks */
|
||||
__syscall_slong_t ppsfreq; /* pps frequency (scaled ppm) (ro) */
|
||||
__syscall_slong_t jitter; /* pps jitter (us) (ro) */
|
||||
int shift; /* interval duration (s) (shift) (ro) */
|
||||
__syscall_slong_t stabil; /* pps stability (scaled ppm) (ro) */
|
||||
__syscall_slong_t jitcnt; /* jitter limit exceeded (ro) */
|
||||
__syscall_slong_t calcnt; /* calibration intervals (ro) */
|
||||
__syscall_slong_t errcnt; /* calibration errors (ro) */
|
||||
__syscall_slong_t stbcnt; /* stability limit exceeded (ro) */
|
||||
|
||||
int tai; /* TAI offset (ro) */
|
||||
|
||||
/* ??? */
|
||||
int :32; int :32; int :32; int :32;
|
||||
int :32; int :32; int :32; int :32;
|
||||
int :32; int :32; int :32;
|
||||
};
|
||||
|
||||
extern __typeof (clock_getres) __clock_getres;
|
||||
extern __typeof (clock_gettime) __clock_gettime;
|
||||
libc_hidden_proto (__clock_gettime)
|
||||
@@ -23,13 +71,22 @@ extern __typeof (clock_settime) __clock_settime;
|
||||
extern __typeof (clock_nanosleep) __clock_nanosleep;
|
||||
extern __typeof (clock_getcpuclockid) __clock_getcpuclockid;
|
||||
|
||||
extern int __clock_gettime64 (clockid_t __clock_id,
|
||||
struct __timespec64 *__tp) __THROW;
|
||||
extern int __clock_settime64 (clockid_t __clock_id,
|
||||
const struct __timespec64 *__tp) __THROW;
|
||||
extern int __clock_getres_time64 (clockid_t __clock_id,
|
||||
struct __timespec64 *__res) __THROW;
|
||||
extern int __clock_nanosleep64 (clockid_t __clock_id, int __flags,
|
||||
const struct __timespec64 *__req,
|
||||
struct __timespec64 *__rem);
|
||||
|
||||
extern int __utimes64 (const char *file,
|
||||
const struct __timeval64 tvp[2]);
|
||||
|
||||
/* Now define the internal interfaces. */
|
||||
struct tm;
|
||||
|
||||
/* time_t variant for representing time zone data, independent of
|
||||
time_t. */
|
||||
typedef __int64_t internal_time_t;
|
||||
|
||||
/* Defined in mktime.c. */
|
||||
extern const unsigned short int __mon_yday[2][13] attribute_hidden;
|
||||
|
||||
@@ -43,35 +100,69 @@ extern int __use_tzfile attribute_hidden;
|
||||
|
||||
extern void __tzfile_read (const char *file, size_t extra,
|
||||
char **extrap) attribute_hidden;
|
||||
extern void __tzfile_compute (internal_time_t timer, int use_localtime,
|
||||
extern void __tzfile_compute (__time64_t timer, int use_localtime,
|
||||
long int *leap_correct, int *leap_hit,
|
||||
struct tm *tp) attribute_hidden;
|
||||
extern void __tzfile_default (const char *std, const char *dst,
|
||||
long int stdoff, long int dstoff)
|
||||
attribute_hidden;
|
||||
extern void __tzset_parse_tz (const char *tz) attribute_hidden;
|
||||
extern void __tz_compute (time_t timer, struct tm *tm, int use_localtime)
|
||||
extern void __tz_compute (__time64_t timer, struct tm *tm, int use_localtime)
|
||||
__THROW attribute_hidden;
|
||||
|
||||
/* Subroutine of `mktime'. Return the `time_t' representation of TP and
|
||||
normalize TP, given that a `struct tm *' maps to a `time_t' as performed
|
||||
/* Subroutine of mktime. Return the __time64_t representation of TP and
|
||||
normalize TP, given that a struct tm * maps to a __time64_t as performed
|
||||
by FUNC. Record next guess for localtime-gmtime offset in *OFFSET. */
|
||||
extern time_t __mktime_internal (struct tm *__tp,
|
||||
struct tm *(*__func) (const time_t *,
|
||||
struct tm *),
|
||||
long int *__offset) attribute_hidden;
|
||||
extern __time64_t __mktime_internal (struct tm *__tp,
|
||||
struct tm *(*__func) (const __time64_t *,
|
||||
struct tm *),
|
||||
long int *__offset) attribute_hidden;
|
||||
|
||||
/* nis/nis_print.c needs ctime, so even if ctime is not declared here,
|
||||
we define __ctime64 as ctime so that nis/nis_print.c can get linked
|
||||
against a function called ctime. */
|
||||
#if __TIMESIZE == 64
|
||||
# define __ctime64 ctime
|
||||
#endif
|
||||
|
||||
#if __TIMESIZE == 64
|
||||
# define __ctime64_r ctime_r
|
||||
#endif
|
||||
|
||||
#if __TIMESIZE == 64
|
||||
# define __localtime64 localtime
|
||||
#else
|
||||
extern struct tm *__localtime64 (const __time64_t *__timer);
|
||||
libc_hidden_proto (__localtime64)
|
||||
#endif
|
||||
|
||||
extern struct tm *__localtime_r (const time_t *__timer,
|
||||
struct tm *__tp) attribute_hidden;
|
||||
|
||||
#if __TIMESIZE == 64
|
||||
# define __localtime64_r __localtime_r
|
||||
#else
|
||||
extern struct tm *__localtime64_r (const __time64_t *__timer,
|
||||
struct tm *__tp) attribute_hidden;
|
||||
#endif
|
||||
|
||||
extern struct tm *__gmtime_r (const time_t *__restrict __timer,
|
||||
struct tm *__restrict __tp);
|
||||
libc_hidden_proto (__gmtime_r)
|
||||
|
||||
/* Compute the `struct tm' representation of *T,
|
||||
#if __TIMESIZE == 64
|
||||
# define __gmtime64 gmtime
|
||||
# define __gmtime64_r __gmtime_r
|
||||
#else
|
||||
extern struct tm *__gmtime64_r (const __time64_t *__restrict __timer,
|
||||
struct tm *__restrict __tp);
|
||||
#endif
|
||||
|
||||
/* Compute the `struct tm' representation of T,
|
||||
offset OFFSET seconds east of UTC,
|
||||
and store year, yday, mon, mday, wday, hour, min, sec into *TP.
|
||||
Return nonzero if successful. */
|
||||
extern int __offtime (const time_t *__timer,
|
||||
extern int __offtime (__time64_t __timer,
|
||||
long int __offset,
|
||||
struct tm *__tp) attribute_hidden;
|
||||
|
||||
@@ -80,8 +171,8 @@ extern char *__asctime_r (const struct tm *__tp, char *__buf)
|
||||
extern void __tzset (void) attribute_hidden;
|
||||
|
||||
/* Prototype for the internal function to get information based on TZ. */
|
||||
extern struct tm *__tz_convert (const time_t *timer, int use_localtime,
|
||||
struct tm *tp) attribute_hidden;
|
||||
extern struct tm *__tz_convert (__time64_t timer, int use_localtime,
|
||||
struct tm *tp) attribute_hidden;
|
||||
|
||||
extern int __nanosleep (const struct timespec *__requested_time,
|
||||
struct timespec *__remaining);
|
||||
@@ -99,6 +190,12 @@ extern char * __strptime_internal (const char *rp, const char *fmt,
|
||||
struct tm *tm, void *statep,
|
||||
locale_t locparam) attribute_hidden;
|
||||
|
||||
#if __TIMESIZE == 64
|
||||
# define __difftime64 __difftime
|
||||
#else
|
||||
extern double __difftime64 (__time64_t time1, __time64_t time0);
|
||||
#endif
|
||||
|
||||
extern double __difftime (time_t time1, time_t time0);
|
||||
|
||||
|
||||
@@ -106,5 +203,98 @@ extern double __difftime (time_t time1, time_t time0);
|
||||
actual clock ID. */
|
||||
#define CLOCK_IDFIELD_SIZE 3
|
||||
|
||||
/* Check whether a time64_t value fits in a time_t. */
|
||||
static inline bool
|
||||
fits_in_time_t (__time64_t t64)
|
||||
{
|
||||
time_t t = t64;
|
||||
return t == t64;
|
||||
}
|
||||
|
||||
/* Convert a known valid struct timeval into a struct __timespec64. */
|
||||
static inline void
|
||||
valid_timeval_to_timespec64 (const struct timeval *tv32,
|
||||
struct __timespec64 *ts64)
|
||||
{
|
||||
ts64->tv_sec = tv32->tv_sec;
|
||||
ts64->tv_nsec = tv32->tv_usec * 1000;
|
||||
}
|
||||
|
||||
/* Convert a known valid struct timespec into a struct __timespec64. */
|
||||
static inline void
|
||||
valid_timespec_to_timespec64 (const struct timespec *ts32,
|
||||
struct __timespec64 *ts64)
|
||||
{
|
||||
ts64->tv_sec = ts32->tv_sec;
|
||||
ts64->tv_nsec = ts32->tv_nsec;
|
||||
/* We only need to zero ts64->tv_pad if we pass it to the kernel. */
|
||||
}
|
||||
|
||||
/* Convert a known valid struct __timespec64 into a struct timespec. */
|
||||
static inline void
|
||||
valid_timespec64_to_timespec (const struct __timespec64 *ts64,
|
||||
struct timespec *ts32)
|
||||
{
|
||||
ts32->tv_sec = (time_t) ts64->tv_sec;
|
||||
ts32->tv_nsec = ts64->tv_nsec;
|
||||
}
|
||||
|
||||
/* Convert a known valid struct __timespec64 into a struct timeval. */
|
||||
static inline void
|
||||
valid_timespec64_to_timeval (const struct __timespec64 *ts64,
|
||||
struct timeval *tv32)
|
||||
{
|
||||
tv32->tv_sec = (time_t) ts64->tv_sec;
|
||||
tv32->tv_usec = ts64->tv_nsec / 1000;
|
||||
}
|
||||
|
||||
/* Check if a value lies with the valid nanoseconds range. */
|
||||
#define IS_VALID_NANOSECONDS(ns) (ns >= 0 && ns <= 999999999)
|
||||
|
||||
/* Check and convert a struct timespec into a struct __timespec64. */
|
||||
static inline bool
|
||||
timespec_to_timespec64 (const struct timespec *ts32,
|
||||
struct __timespec64 *ts64)
|
||||
{
|
||||
/* Check that ts32 holds a valid count of nanoseconds. */
|
||||
if (! IS_VALID_NANOSECONDS (ts32->tv_nsec))
|
||||
return false;
|
||||
/* All ts32 fields can fit in ts64, so copy them. */
|
||||
valid_timespec_to_timespec64 (ts32, ts64);
|
||||
/* We only need to zero ts64->tv_pad if we pass it to the kernel. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check and convert a struct __timespec64 into a struct timespec. */
|
||||
static inline bool
|
||||
timespec64_to_timespec (const struct __timespec64 *ts64,
|
||||
struct timespec *ts32)
|
||||
{
|
||||
/* Check that tv_nsec holds a valid count of nanoseconds. */
|
||||
if (! IS_VALID_NANOSECONDS (ts64->tv_nsec))
|
||||
return false;
|
||||
/* Check that tv_sec can fit in a __time_t. */
|
||||
if (! fits_in_time_t (ts64->tv_sec))
|
||||
return false;
|
||||
/* All ts64 fields can fit in ts32, so copy them. */
|
||||
valid_timespec64_to_timespec (ts64, ts32);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check and convert a struct __timespec64 into a struct timeval. */
|
||||
static inline bool
|
||||
timespec64_to_timeval (const struct __timespec64 *ts64,
|
||||
struct timeval *tv32)
|
||||
{
|
||||
/* Check that tv_nsec holds a valid count of nanoseconds. */
|
||||
if (! IS_VALID_NANOSECONDS (ts64->tv_nsec))
|
||||
return false;
|
||||
/* Check that tv_sec can fit in a __time_t. */
|
||||
if (! fits_in_time_t (ts64->tv_sec))
|
||||
return false;
|
||||
/* All ts64 fields can fit in tv32, so copy them. */
|
||||
valid_timespec64_to_timeval (ts64, tv32);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -6,4 +6,11 @@
|
||||
libc_hidden_proto (utime)
|
||||
#endif
|
||||
|
||||
/* Structure describing file times, 64-bit time version. */
|
||||
struct __utimbuf64
|
||||
{
|
||||
__time64_t actime; /* Access time. */
|
||||
__time64_t modtime; /* Modification time. */
|
||||
};
|
||||
|
||||
#endif /* utime.h */
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ headers := sys/stat.h bits/stat.h sys/statfs.h bits/statfs.h sys/vfs.h \
|
||||
bits/statx.h utime.h ftw.h fts.h sys/sendfile.h
|
||||
|
||||
routines := \
|
||||
utime \
|
||||
utime utime64 \
|
||||
mkfifo mkfifoat \
|
||||
stat fstat lstat stat64 fstat64 lstat64 fstatat fstatat64 \
|
||||
xstat fxstat lxstat xstat64 fxstat64 lxstat64 statx \
|
||||
|
||||
@@ -132,6 +132,13 @@ libc {
|
||||
fcntl64;
|
||||
statx;
|
||||
}
|
||||
GLIBC_2.29 {
|
||||
__fxstat64_time64;
|
||||
__xstat64_time64;
|
||||
__lxstat64_time64;
|
||||
__fxstatat64_time64;
|
||||
__utime64;
|
||||
}
|
||||
GLIBC_PRIVATE {
|
||||
__libc_fcntl64;
|
||||
__fcntl_nocancel;
|
||||
|
||||
@@ -50,3 +50,10 @@ fstat64 (int fd, struct stat64 *buf)
|
||||
{
|
||||
return __fxstat64 (_STAT_VER, fd, buf);
|
||||
}
|
||||
|
||||
int
|
||||
attribute_hidden
|
||||
__fstat64_time64 (int fd, struct __stat64_t64 *buf)
|
||||
{
|
||||
return __fxstat64_time64 (_STAT_VER, fd, buf);
|
||||
}
|
||||
|
||||
@@ -50,3 +50,10 @@ fstatat64 (int fd, const char *file, struct stat64 *buf, int flag)
|
||||
{
|
||||
return __fxstatat64 (_STAT_VER, fd, file, buf, flag);
|
||||
}
|
||||
|
||||
int
|
||||
attribute_hidden
|
||||
__fstatat64_time64 (int fd, const char *file, struct __stat64_t64 *buf, int flag)
|
||||
{
|
||||
return __fxstatat64_time64 (_STAT_VER, fd, file, buf, flag);
|
||||
}
|
||||
|
||||
@@ -32,3 +32,11 @@ futimens (int fd, const struct timespec tsp[2])
|
||||
return -1;
|
||||
}
|
||||
stub_warning (futimens)
|
||||
|
||||
int
|
||||
__futimens64 (int fd, const struct __timespec64 tsp[2])
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
stub_warning (__futimens64)
|
||||
|
||||
@@ -50,3 +50,10 @@ lstat64 (const char *file, struct stat64 *buf)
|
||||
{
|
||||
return __lxstat64 (_STAT_VER, file, buf);
|
||||
}
|
||||
|
||||
int
|
||||
attribute_hidden
|
||||
__lstat64_time64 (const char *file, struct __stat64_t64 *buf)
|
||||
{
|
||||
return __lxstat64_time64 (_STAT_VER, file, buf);
|
||||
}
|
||||
|
||||
@@ -50,3 +50,10 @@ stat64 (const char *file, struct stat64 *buf)
|
||||
{
|
||||
return __xstat64 (_STAT_VER, file, buf);
|
||||
}
|
||||
|
||||
int
|
||||
attribute_hidden
|
||||
__stat64_time64 (const char *file, struct __stat64_t64 *buf)
|
||||
{
|
||||
return __xstat64_time64 (_STAT_VER, file, buf);
|
||||
}
|
||||
|
||||
+69
-6
@@ -210,14 +210,27 @@ extern int stat (const char *__restrict __file,
|
||||
extern int fstat (int __fd, struct stat *__buf) __THROW __nonnull ((2));
|
||||
#else
|
||||
# ifdef __REDIRECT_NTH
|
||||
# ifdef __USE_TIME_BITS64
|
||||
extern int __REDIRECT_NTH (stat, (const char *__restrict __file,
|
||||
struct stat *__restrict __buf), __stat64_time64)
|
||||
__nonnull ((1, 2));
|
||||
extern int __REDIRECT_NTH (fstat, (int __fd, struct stat *__buf), __fstat64_time64)
|
||||
__nonnull ((2));
|
||||
# else
|
||||
extern int __REDIRECT_NTH (stat, (const char *__restrict __file,
|
||||
struct stat *__restrict __buf), stat64)
|
||||
__nonnull ((1, 2));
|
||||
extern int __REDIRECT_NTH (fstat, (int __fd, struct stat *__buf), fstat64)
|
||||
__nonnull ((2));
|
||||
# endif
|
||||
# else
|
||||
# define stat stat64
|
||||
# define fstat fstat64
|
||||
# ifdef __USE_TIME_BITS64
|
||||
# define stat stat64_time64
|
||||
# define fstat fstat64_time64
|
||||
# else
|
||||
# define stat stat64
|
||||
# define fstat fstat64
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#ifdef __USE_LARGEFILE64
|
||||
@@ -260,12 +273,23 @@ extern int lstat (const char *__restrict __file,
|
||||
struct stat *__restrict __buf) __THROW __nonnull ((1, 2));
|
||||
# else
|
||||
# ifdef __REDIRECT_NTH
|
||||
# ifdef __USE_TIME_BITS64
|
||||
extern int __REDIRECT_NTH (lstat,
|
||||
(const char *__restrict __file,
|
||||
struct stat *__restrict __buf), __lstat64_time64)
|
||||
__nonnull ((1, 2));
|
||||
# else
|
||||
extern int __REDIRECT_NTH (lstat,
|
||||
(const char *__restrict __file,
|
||||
struct stat *__restrict __buf), lstat64)
|
||||
__nonnull ((1, 2));
|
||||
# endif
|
||||
# else
|
||||
# define lstat lstat64
|
||||
# ifdef __USE_TIME_BITS64
|
||||
# define lstat __lstat64_time64
|
||||
# else
|
||||
# define lstat lstat64
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
# ifdef __USE_LARGEFILE64
|
||||
@@ -357,6 +381,15 @@ extern int mkfifoat (int __fd, const char *__path, __mode_t __mode)
|
||||
#ifdef __USE_ATFILE
|
||||
/* Set file access and modification times relative to directory file
|
||||
descriptor. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (utimensat, (int __fd, const char *__path,
|
||||
const struct timespec __times[2], int __flags),
|
||||
__utimensat64) __THROW __nonnull((2));
|
||||
# else
|
||||
# define utimensat __utimensat64
|
||||
# endif
|
||||
#endif
|
||||
extern int utimensat (int __fd, const char *__path,
|
||||
const struct timespec __times[2],
|
||||
int __flags)
|
||||
@@ -365,6 +398,14 @@ extern int utimensat (int __fd, const char *__path,
|
||||
|
||||
#ifdef __USE_XOPEN2K8
|
||||
/* Set file access and modification times of the file associated with FD. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (futimens, (int __fd, const struct timespec __times[2]),
|
||||
__futimens64) __THROW;
|
||||
# else
|
||||
# define futimens __futimens64
|
||||
# endif
|
||||
#endif
|
||||
extern int futimens (int __fd, const struct timespec __times[2]) __THROW;
|
||||
#endif
|
||||
|
||||
@@ -403,6 +444,21 @@ extern int __fxstatat (int __ver, int __fildes, const char *__filename,
|
||||
__THROW __nonnull ((3, 4));
|
||||
#else
|
||||
# ifdef __REDIRECT_NTH
|
||||
# ifdef __USE_TIME_BITS64
|
||||
extern int __REDIRECT_NTH (__fxstat, (int __ver, int __fildes,
|
||||
struct stat *__stat_buf), __fxstat64_time64)
|
||||
__nonnull ((3));
|
||||
extern int __REDIRECT_NTH (__xstat, (int __ver, const char *__filename,
|
||||
struct stat *__stat_buf), __xstat64_time64)
|
||||
__nonnull ((2, 3));
|
||||
extern int __REDIRECT_NTH (__lxstat, (int __ver, const char *__filename,
|
||||
struct stat *__stat_buf), __lxstat64_time64)
|
||||
__nonnull ((2, 3));
|
||||
extern int __REDIRECT_NTH (__fxstatat, (int __ver, int __fildes,
|
||||
const char *__filename,
|
||||
struct stat *__stat_buf, int __flag),
|
||||
__fxstatat64_time64) __nonnull ((3, 4));
|
||||
# else
|
||||
extern int __REDIRECT_NTH (__fxstat, (int __ver, int __fildes,
|
||||
struct stat *__stat_buf), __fxstat64)
|
||||
__nonnull ((3));
|
||||
@@ -416,11 +472,18 @@ extern int __REDIRECT_NTH (__fxstatat, (int __ver, int __fildes,
|
||||
const char *__filename,
|
||||
struct stat *__stat_buf, int __flag),
|
||||
__fxstatat64) __nonnull ((3, 4));
|
||||
# endif
|
||||
|
||||
# else
|
||||
# define __fxstat __fxstat64
|
||||
# define __xstat __xstat64
|
||||
# define __lxstat __lxstat64
|
||||
# ifdef __USE_TIME_BITS64
|
||||
# define __fxstat __fxstat64_time64
|
||||
# define __xstat __xstat64_time64
|
||||
# define __lxstat __lxstat64_time64
|
||||
# else
|
||||
# define __fxstat __fxstat64
|
||||
# define __xstat __xstat64
|
||||
# define __lxstat __lxstat64
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
+16
@@ -38,3 +38,19 @@ utime (const char *file, const struct utimbuf *times)
|
||||
libc_hidden_def (utime)
|
||||
|
||||
stub_warning (utime)
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__utime64 (const char *file, const struct __utimbuf64 *times)
|
||||
{
|
||||
if (file == NULL)
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
stub_warning (__utime64)
|
||||
|
||||
+13
-3
@@ -27,20 +27,30 @@
|
||||
__BEGIN_DECLS
|
||||
|
||||
#include <bits/types.h>
|
||||
#include <bits/types/time_t.h>
|
||||
|
||||
#if defined __USE_XOPEN || defined __USE_XOPEN2K
|
||||
# include <bits/types/time_t.h>
|
||||
#endif
|
||||
|
||||
/* Structure describing file times. */
|
||||
/* Structure describing file times, 32- or 64-bit time. */
|
||||
struct utimbuf
|
||||
{
|
||||
__time_t actime; /* Access time. */
|
||||
__time_t modtime; /* Modification time. */
|
||||
time_t actime; /* Access time. */
|
||||
time_t modtime; /* Modification time. */
|
||||
};
|
||||
|
||||
/* Set the access and modification times of FILE to those given in
|
||||
*FILE_TIMES. If FILE_TIMES is NULL, set them to the current time. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (utime, (const char *__file,
|
||||
const struct utimbuf *__file_times),
|
||||
__utime64) __THROW __nonnull ((1));
|
||||
# else
|
||||
# define utime __utime64
|
||||
# endif
|
||||
#endif
|
||||
extern int utime (const char *__file,
|
||||
const struct utimbuf *__file_times)
|
||||
__THROW __nonnull ((1));
|
||||
|
||||
@@ -30,3 +30,12 @@ utimensat (int fd, const char *file, const struct timespec tsp[2],
|
||||
return -1;
|
||||
}
|
||||
stub_warning (utimensat)
|
||||
|
||||
int
|
||||
__utimensat_time64 (int fd, const char *file, const struct __timespec64 tsp[2],
|
||||
int flags)
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
stub_warning (__utimensat_time64)
|
||||
|
||||
@@ -165,6 +165,34 @@ This macro was introduced as part of the Large File Support extension
|
||||
(LFS).
|
||||
@end defvr
|
||||
|
||||
@defvr Macro _TIME_BITS
|
||||
This macro determines the bit size of @code{time_t} (and therefore the
|
||||
bit size of all @code{time_t} derived types and the prototypes of all
|
||||
related functions). If @code{_TIME_BITS} is undefined, the bit size of
|
||||
time_t equals the bit size of the architecture.
|
||||
|
||||
If @code{_TIME_BITS} is undefined, or if @code{_TIME_BITS} is defined
|
||||
to the value @code{32} and @code{__WORDSIZE} is defined to the value
|
||||
@code{32}, or or if @code{_TIME_BITS} is defined to the value @code{64}
|
||||
and @code{__WORDSIZE} is defined to the value @code{64}, nothing changes.
|
||||
|
||||
If @code{_TIME_BITS} is defined to the value @code{64} and if
|
||||
@code{__WORDSIZE} is defined to the value @code{32}, then the @w{64 bit}
|
||||
time API and implementation are used even though the architecture word
|
||||
size is @code{32}. Also, if the kernel provides @w{64 bit} time support,
|
||||
it is used; otherwise, the @w{32 bit} kernel time support is used (with
|
||||
no provision to address kernel Y2038 shortcomings).
|
||||
|
||||
If @code{_TIME_BITS} is defined to the value @code{32} and if
|
||||
@code{__WORDSIZE} is defined to the value @code{64}, then a compile-time
|
||||
error is emitted.
|
||||
|
||||
If @code{_TIME_BITS} is defined to a value different from both @code{32}
|
||||
and @code{64}, then a compile-time error is emitted.
|
||||
|
||||
This macro was introduced as part of the Y2038 support.
|
||||
@end defvr
|
||||
|
||||
@defvr Macro _ISOC99_SOURCE
|
||||
@standards{GNU, (none)}
|
||||
If this macro is defined, features from ISO C99 are included. Since
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ routines := brk sbrk sstk ioctl \
|
||||
fgetxattr flistxattr fremovexattr fsetxattr getxattr \
|
||||
listxattr lgetxattr llistxattr lremovexattr lsetxattr \
|
||||
removexattr setxattr getauxval ifunc-impl-list makedev \
|
||||
allocate_once
|
||||
allocate_once y2038-support utimes64
|
||||
|
||||
generated += tst-error1.mtrace tst-error1-mem.out \
|
||||
tst-allocate_once.mtrace tst-allocate_once-mem.out
|
||||
|
||||
@@ -158,6 +158,8 @@ libc {
|
||||
GLIBC_2.26 {
|
||||
preadv2; preadv64v2; pwritev2; pwritev64v2;
|
||||
}
|
||||
GLIBC_2.29 {
|
||||
}
|
||||
GLIBC_PRIVATE {
|
||||
__madvise;
|
||||
__mktemp;
|
||||
@@ -166,5 +168,7 @@ libc {
|
||||
__mmap; __munmap; __mprotect;
|
||||
__sched_get_priority_min; __sched_get_priority_max;
|
||||
__libc_allocate_once_slow;
|
||||
__y2038_get_kernel_support;
|
||||
__y2038_set_kernel_support;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,3 +30,12 @@ __futimes (int fd, const struct timeval tvp[2])
|
||||
weak_alias (__futimes, futimes)
|
||||
|
||||
stub_warning (futimes)
|
||||
|
||||
int
|
||||
__futimes64 (int fd, const struct __timeval64 tvp[2])
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stub_warning (__futimes64)
|
||||
|
||||
@@ -31,3 +31,11 @@ __lutimes (const char *file, const struct timeval tvp[2])
|
||||
weak_alias (__lutimes, lutimes)
|
||||
|
||||
stub_warning (lutimes)
|
||||
|
||||
int
|
||||
__lutimes64 (const char *file, const struct __timeval64 tvp[2])
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
stub_warning (__lutimes64)
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
/* Get definition of timer specification structures. */
|
||||
#include <bits/types/time_t.h>
|
||||
#include <bits/types/struct_timeval.h>
|
||||
#include <bits/types/struct_timeval64.h>
|
||||
#ifdef __USE_XOPEN2K
|
||||
# include <bits/types/struct_timespec.h>
|
||||
#endif
|
||||
@@ -98,6 +99,18 @@ __BEGIN_DECLS
|
||||
|
||||
This function is a cancellation point and therefore not marked with
|
||||
__THROW. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (select, (int __nfds,
|
||||
fd_set *__restrict __readfds,
|
||||
fd_set *__restrict __writefds,
|
||||
fd_set *__restrict __exceptfds,
|
||||
struct timeval *__restrict __timeout),
|
||||
__select64);
|
||||
# else
|
||||
# define select __select64
|
||||
# endif
|
||||
#endif
|
||||
extern int select (int __nfds, fd_set *__restrict __readfds,
|
||||
fd_set *__restrict __writefds,
|
||||
fd_set *__restrict __exceptfds,
|
||||
@@ -110,6 +123,19 @@ extern int select (int __nfds, fd_set *__restrict __readfds,
|
||||
|
||||
This function is a cancellation point and therefore not marked with
|
||||
__THROW. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (pselect, (int __nfds,
|
||||
fd_set *__restrict __readfds,
|
||||
fd_set *__restrict __writefds,
|
||||
fd_set *__restrict __exceptfds,
|
||||
const struct timespec *__restrict __timeout,
|
||||
const __sigset_t *__restrict __sigmask),
|
||||
__pselect64);
|
||||
# else
|
||||
# define pselect __pselect64
|
||||
# endif
|
||||
#endif
|
||||
extern int pselect (int __nfds, fd_set *__restrict __readfds,
|
||||
fd_set *__restrict __writefds,
|
||||
fd_set *__restrict __exceptfds,
|
||||
|
||||
@@ -38,3 +38,18 @@ __utimes (const char *file, const struct timeval tvp[2])
|
||||
weak_alias (__utimes, utimes)
|
||||
|
||||
stub_warning (utimes)
|
||||
|
||||
int
|
||||
__utimes64 (const char *file, const struct __timeval64 tvp[2])
|
||||
{
|
||||
if (file == NULL)
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stub_warning (__utimes64)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/* y2038 general kernel support indication.
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef __ASSUME_KERNEL_Y2038_SUPPORT
|
||||
|
||||
/* By default glibc assumes the underlying kernel does not support Y2038. */
|
||||
bool
|
||||
__default_y2038_get_kernel_support (void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
weak_alias (__default_y2038_get_kernel_support, __y2038_get_kernel_support)
|
||||
|
||||
/* By default glibc just ignores Y2038 support indication setting. */
|
||||
void
|
||||
__default_y2038_set_kernel_support (bool support __attribute__ ((unused)))
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
weak_alias (__default_y2038_set_kernel_support, __y2038_set_kernel_support)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
/* y2038 general kernel support indication.
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* If we do not assume that the kernel provides 64-bit-time syscalls,
|
||||
* then we need a dynamic way to remember whether it does or not. */
|
||||
|
||||
#ifndef __ASSUME_KERNEL_Y2038_SUPPORT
|
||||
|
||||
/* Dynamically check whether actual kernel supports Y2038 or not. */
|
||||
extern bool __y2038_get_kernel_support (void);
|
||||
|
||||
/* Dynamically set whether actual kernel supports Y2038 or not. */
|
||||
extern void __y2038_set_kernel_support (bool support);
|
||||
|
||||
#endif
|
||||
@@ -269,6 +269,7 @@ libpthread {
|
||||
GLIBC_2.22 {
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
# C11 thread symbols.
|
||||
GLIBC_2.28 {
|
||||
thrd_create; thrd_detach; thrd_exit; thrd_join;
|
||||
@@ -277,6 +278,14 @@ libpthread {
|
||||
cnd_timedwait; cnd_wait; tss_create; tss_delete; tss_get; tss_set;
|
||||
}
|
||||
|
||||
GLIBC_2.29 {
|
||||
__pthread_rwlock_rdlock64;
|
||||
__pthread_rwlock_wrlock64;
|
||||
__pthread_mutex_timedlock64;
|
||||
__sem_timedwait64;
|
||||
__pthread_cond_timedwait64;
|
||||
}
|
||||
|
||||
GLIBC_PRIVATE {
|
||||
__pthread_initialize_minimal;
|
||||
__pthread_clock_gettime; __pthread_clock_settime;
|
||||
|
||||
@@ -57,3 +57,40 @@ __lll_timedlock_wait (int *futex, const struct timespec *abstime, int private)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__lll_timedlock_wait64 (int *futex, const struct __timespec64 *abstime, int private)
|
||||
{
|
||||
/* Reject invalid timeouts. */
|
||||
if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
||||
return EINVAL;
|
||||
|
||||
/* Try locking. */
|
||||
while (atomic_exchange_acq (futex, 2) != 0)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
/* Get the current time. */
|
||||
(void) __gettimeofday (&tv, NULL);
|
||||
|
||||
/* Compute relative timeout. */
|
||||
struct timespec rt;
|
||||
rt.tv_sec = abstime->tv_sec - tv.tv_sec;
|
||||
rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
|
||||
if (rt.tv_nsec < 0)
|
||||
{
|
||||
rt.tv_nsec += 1000000000;
|
||||
--rt.tv_sec;
|
||||
}
|
||||
|
||||
if (rt.tv_sec < 0)
|
||||
return ETIMEDOUT;
|
||||
|
||||
/* If *futex == 2, wait until woken or timeout. */
|
||||
lll_futex_timed_wait (futex, 2, &rt, private);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <bits/types/struct_timespec64.h>
|
||||
#include "pthreadP.h"
|
||||
|
||||
|
||||
#if HP_TIMING_AVAIL
|
||||
int
|
||||
__pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq,
|
||||
struct timespec *tp)
|
||||
__pthread_clock_gettime64 (clockid_t clock_id, hp_timing_t freq,
|
||||
struct __timespec64 *tp)
|
||||
{
|
||||
hp_timing_t tsc;
|
||||
|
||||
@@ -64,4 +65,36 @@ __pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
__pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq,
|
||||
struct timespec *tp)
|
||||
{
|
||||
struct __timespec64 ts64;
|
||||
int res;
|
||||
|
||||
if (tp == NULL)
|
||||
{
|
||||
__set_errno(EINVAL);
|
||||
res = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int res = __pthread_clock_gettime64 (clock_id, freq, &ts64);
|
||||
if (res == 0)
|
||||
{
|
||||
if (fits_in_time_t (ts64.tv_time))
|
||||
{
|
||||
tp->tv_sec = ts64.tv_sec;
|
||||
tp->tv_nsec = ts64.tv_nsec;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_errno(EOVERFLOW);
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -647,6 +647,280 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
return (err != 0) ? err : result;
|
||||
}
|
||||
|
||||
/* 64-bit time variant */
|
||||
|
||||
static __always_inline int
|
||||
__pthread_cond_wait_common64 (pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
const struct __timespec64 *abstime)
|
||||
{
|
||||
const int maxspin = 0;
|
||||
int err;
|
||||
int result = 0;
|
||||
|
||||
LIBC_PROBE (cond_wait, 2, cond, mutex);
|
||||
|
||||
/* Acquire a position (SEQ) in the waiter sequence (WSEQ). We use an
|
||||
atomic operation because signals and broadcasts may update the group
|
||||
switch without acquiring the mutex. We do not need release MO here
|
||||
because we do not need to establish any happens-before relation with
|
||||
signalers (see __pthread_cond_signal); modification order alone
|
||||
establishes a total order of waiters/signals. We do need acquire MO
|
||||
to synchronize with group reinitialization in
|
||||
__condvar_quiesce_and_switch_g1. */
|
||||
uint64_t wseq = __condvar_fetch_add_wseq_acquire (cond, 2);
|
||||
/* Find our group's index. We always go into what was G2 when we acquired
|
||||
our position. */
|
||||
unsigned int g = wseq & 1;
|
||||
uint64_t seq = wseq >> 1;
|
||||
|
||||
/* Increase the waiter reference count. Relaxed MO is sufficient because
|
||||
we only need to synchronize when decrementing the reference count. */
|
||||
unsigned int flags = atomic_fetch_add_relaxed (&cond->__data.__wrefs, 8);
|
||||
int private = __condvar_get_private (flags);
|
||||
|
||||
/* Now that we are registered as a waiter, we can release the mutex.
|
||||
Waiting on the condvar must be atomic with releasing the mutex, so if
|
||||
the mutex is used to establish a happens-before relation with any
|
||||
signaler, the waiter must be visible to the latter; thus, we release the
|
||||
mutex after registering as waiter.
|
||||
If releasing the mutex fails, we just cancel our registration as a
|
||||
waiter and confirm that we have woken up. */
|
||||
err = __pthread_mutex_unlock_usercnt (mutex, 0);
|
||||
if (__glibc_unlikely (err != 0))
|
||||
{
|
||||
__condvar_cancel_waiting (cond, seq, g, private);
|
||||
__condvar_confirm_wakeup (cond, private);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Now wait until a signal is available in our group or it is closed.
|
||||
Acquire MO so that if we observe a value of zero written after group
|
||||
switching in __condvar_quiesce_and_switch_g1, we synchronize with that
|
||||
store and will see the prior update of __g1_start done while switching
|
||||
groups too. */
|
||||
unsigned int signals = atomic_load_acquire (cond->__data.__g_signals + g);
|
||||
|
||||
do
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
/* Spin-wait first.
|
||||
Note that spinning first without checking whether a timeout
|
||||
passed might lead to what looks like a spurious wake-up even
|
||||
though we should return ETIMEDOUT (e.g., if the caller provides
|
||||
an absolute timeout that is clearly in the past). However,
|
||||
(1) spurious wake-ups are allowed, (2) it seems unlikely that a
|
||||
user will (ab)use pthread_cond_wait as a check for whether a
|
||||
point in time is in the past, and (3) spinning first without
|
||||
having to compare against the current time seems to be the right
|
||||
choice from a performance perspective for most use cases. */
|
||||
unsigned int spin = maxspin;
|
||||
while (signals == 0 && spin > 0)
|
||||
{
|
||||
/* Check that we are not spinning on a group that's already
|
||||
closed. */
|
||||
if (seq < (__condvar_load_g1_start_relaxed (cond) >> 1))
|
||||
goto done;
|
||||
|
||||
/* TODO Back off. */
|
||||
|
||||
/* Reload signals. See above for MO. */
|
||||
signals = atomic_load_acquire (cond->__data.__g_signals + g);
|
||||
spin--;
|
||||
}
|
||||
|
||||
/* If our group will be closed as indicated by the flag on signals,
|
||||
don't bother grabbing a signal. */
|
||||
if (signals & 1)
|
||||
goto done;
|
||||
|
||||
/* If there is an available signal, don't block. */
|
||||
if (signals != 0)
|
||||
break;
|
||||
|
||||
/* No signals available after spinning, so prepare to block.
|
||||
We first acquire a group reference and use acquire MO for that so
|
||||
that we synchronize with the dummy read-modify-write in
|
||||
__condvar_quiesce_and_switch_g1 if we read from that. In turn,
|
||||
in this case this will make us see the closed flag on __g_signals
|
||||
that designates a concurrent attempt to reuse the group's slot.
|
||||
We use acquire MO for the __g_signals check to make the
|
||||
__g1_start check work (see spinning above).
|
||||
Note that the group reference acquisition will not mask the
|
||||
release MO when decrementing the reference count because we use
|
||||
an atomic read-modify-write operation and thus extend the release
|
||||
sequence. */
|
||||
atomic_fetch_add_acquire (cond->__data.__g_refs + g, 2);
|
||||
if (((atomic_load_acquire (cond->__data.__g_signals + g) & 1) != 0)
|
||||
|| (seq < (__condvar_load_g1_start_relaxed (cond) >> 1)))
|
||||
{
|
||||
/* Our group is closed. Wake up any signalers that might be
|
||||
waiting. */
|
||||
__condvar_dec_grefs (cond, g, private);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Now block.
|
||||
struct _pthread_cleanup_buffer buffer;
|
||||
struct _condvar_cleanup_buffer cbuffer;
|
||||
cbuffer.wseq = wseq;
|
||||
cbuffer.cond = cond;
|
||||
cbuffer.mutex = mutex;
|
||||
cbuffer.private = private;
|
||||
__pthread_cleanup_push (&buffer, __condvar_cleanup_waiting, &cbuffer);
|
||||
|
||||
if (abstime == NULL)
|
||||
{
|
||||
/* Block without a timeout. */
|
||||
err = futex_wait_cancelable (
|
||||
cond->__data.__g_signals + g, 0, private);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Block, but with a timeout.
|
||||
Work around the fact that the kernel rejects negative timeout
|
||||
values despite them being valid. */
|
||||
if (__glibc_unlikely (abstime->tv_sec < 0))
|
||||
err = ETIMEDOUT;
|
||||
|
||||
else if ((flags & __PTHREAD_COND_CLOCK_MONOTONIC_MASK) != 0)
|
||||
{
|
||||
/* CLOCK_MONOTONIC is requested. */
|
||||
struct timespec rt;
|
||||
struct __timespec64 rt64;
|
||||
if (__clock_gettime (CLOCK_MONOTONIC, &rt) != 0)
|
||||
__libc_fatal ("clock_gettime does not support "
|
||||
"CLOCK_MONOTONIC");
|
||||
/* Convert the absolute timeout value to a relative
|
||||
timeout. */
|
||||
rt64.tv_sec = abstime->tv_sec - rt.tv_sec;
|
||||
rt64.tv_nsec = abstime->tv_nsec - rt.tv_nsec;
|
||||
if (rt64.tv_nsec < 0)
|
||||
{
|
||||
rt64.tv_nsec += 1000000000;
|
||||
--rt64.tv_sec;
|
||||
}
|
||||
/* Did we already time out? */
|
||||
if (__glibc_unlikely (rt64.tv_sec < 0))
|
||||
err = ETIMEDOUT;
|
||||
else
|
||||
err = futex_reltimed_wait_cancelable64
|
||||
(cond->__data.__g_signals + g, 0, &rt64, private);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use CLOCK_REALTIME. */
|
||||
err = futex_abstimed_wait_cancelable64
|
||||
(cond->__data.__g_signals + g, 0, abstime, private);
|
||||
}
|
||||
}
|
||||
|
||||
__pthread_cleanup_pop (&buffer, 0);
|
||||
|
||||
if (__glibc_unlikely (err == ETIMEDOUT))
|
||||
{
|
||||
__condvar_dec_grefs (cond, g, private);
|
||||
/* If we timed out, we effectively cancel waiting. Note that
|
||||
we have decremented __g_refs before cancellation, so that a
|
||||
deadlock between waiting for quiescence of our group in
|
||||
__condvar_quiesce_and_switch_g1 and us trying to acquire
|
||||
the lock during cancellation is not possible. */
|
||||
__condvar_cancel_waiting (cond, seq, g, private);
|
||||
result = ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
__condvar_dec_grefs (cond, g, private);
|
||||
|
||||
/* Reload signals. See above for MO. */
|
||||
signals = atomic_load_acquire (cond->__data.__g_signals + g);
|
||||
}
|
||||
|
||||
}
|
||||
/* Try to grab a signal. Use acquire MO so that we see an up-to-date value
|
||||
of __g1_start below (see spinning above for a similar case). In
|
||||
particular, if we steal from a more recent group, we will also see a
|
||||
more recent __g1_start below. */
|
||||
while (!atomic_compare_exchange_weak_acquire (cond->__data.__g_signals + g,
|
||||
&signals, signals - 2));
|
||||
|
||||
/* We consumed a signal but we could have consumed from a more recent group
|
||||
that aliased with ours due to being in the same group slot. If this
|
||||
might be the case our group must be closed as visible through
|
||||
__g1_start. */
|
||||
uint64_t g1_start = __condvar_load_g1_start_relaxed (cond);
|
||||
if (seq < (g1_start >> 1))
|
||||
{
|
||||
/* We potentially stole a signal from a more recent group but we do not
|
||||
know which group we really consumed from.
|
||||
We do not care about groups older than current G1 because they are
|
||||
closed; we could have stolen from these, but then we just add a
|
||||
spurious wake-up for the current groups.
|
||||
We will never steal a signal from current G2 that was really intended
|
||||
for G2 because G2 never receives signals (until it becomes G1). We
|
||||
could have stolen a signal from G2 that was conservatively added by a
|
||||
previous waiter that also thought it stole a signal -- but given that
|
||||
that signal was added unnecessarily, it's not a problem if we steal
|
||||
it.
|
||||
Thus, the remaining case is that we could have stolen from the current
|
||||
G1, where "current" means the __g1_start value we observed. However,
|
||||
if the current G1 does not have the same slot index as we do, we did
|
||||
not steal from it and do not need to undo that. This is the reason
|
||||
for putting a bit with G2's index into__g1_start as well. */
|
||||
if (((g1_start & 1) ^ 1) == g)
|
||||
{
|
||||
/* We have to conservatively undo our potential mistake of stealing
|
||||
a signal. We can stop trying to do that when the current G1
|
||||
changes because other spinning waiters will notice this too and
|
||||
__condvar_quiesce_and_switch_g1 has checked that there are no
|
||||
futex waiters anymore before switching G1.
|
||||
Relaxed MO is fine for the __g1_start load because we need to
|
||||
merely be able to observe this fact and not have to observe
|
||||
something else as well.
|
||||
??? Would it help to spin for a little while to see whether the
|
||||
current G1 gets closed? This might be worthwhile if the group is
|
||||
small or close to being closed. */
|
||||
unsigned int s = atomic_load_relaxed (cond->__data.__g_signals + g);
|
||||
while (__condvar_load_g1_start_relaxed (cond) == g1_start)
|
||||
{
|
||||
/* Try to add a signal. We don't need to acquire the lock
|
||||
because at worst we can cause a spurious wake-up. If the
|
||||
group is in the process of being closed (LSB is true), this
|
||||
has an effect similar to us adding a signal. */
|
||||
if (((s & 1) != 0)
|
||||
|| atomic_compare_exchange_weak_relaxed
|
||||
(cond->__data.__g_signals + g, &s, s + 2))
|
||||
{
|
||||
/* If we added a signal, we also need to add a wake-up on
|
||||
the futex. We also need to do that if we skipped adding
|
||||
a signal because the group is being closed because
|
||||
while __condvar_quiesce_and_switch_g1 could have closed
|
||||
the group, it might stil be waiting for futex waiters to
|
||||
leave (and one of those waiters might be the one we stole
|
||||
the signal from, which cause it to block using the
|
||||
futex). */
|
||||
futex_wake (cond->__data.__g_signals + g, 1, private);
|
||||
break;
|
||||
}
|
||||
/* TODO Back off. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
/* Confirm that we have been woken. We do that before acquiring the mutex
|
||||
to allow for execution of pthread_cond_destroy while having acquired the
|
||||
mutex. */
|
||||
__condvar_confirm_wakeup (cond, private);
|
||||
|
||||
/* Woken up; now re-acquire the mutex. If this doesn't fail, return RESULT,
|
||||
which is set to ETIMEDOUT if a timeout occured, or zero otherwise. */
|
||||
err = __pthread_mutex_cond_lock (mutex);
|
||||
/* XXX Abort on errors that are disallowed by POSIX? */
|
||||
return (err != 0) ? err : result;
|
||||
}
|
||||
|
||||
|
||||
/* See __pthread_cond_wait_common. */
|
||||
int
|
||||
@@ -667,6 +941,17 @@ __pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
return __pthread_cond_wait_common (cond, mutex, abstime);
|
||||
}
|
||||
|
||||
int
|
||||
__pthread_cond_timedwait64 (pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
const struct __timespec64 *abstime)
|
||||
{
|
||||
/* Check parameter validity. This should also tell the compiler that
|
||||
it can assume that abstime is not NULL. */
|
||||
if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
||||
return EINVAL;
|
||||
return __pthread_cond_wait_common64 (cond, mutex, abstime);
|
||||
}
|
||||
|
||||
versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
|
||||
GLIBC_2_3_2);
|
||||
versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <atomic.h>
|
||||
#include <lowlevellock.h>
|
||||
#include <not-cancel.h>
|
||||
#include <y2038-support.h>
|
||||
|
||||
#include <stap-probe.h>
|
||||
|
||||
@@ -32,6 +33,10 @@
|
||||
#define lll_timedlock_elision(a,dummy,b,c) lll_timedlock(a, b, c)
|
||||
#endif
|
||||
|
||||
#ifndef lll_timedlock_elision64
|
||||
#define lll_timedlock_elision64(a,dummy,b,c) lll_timedlock64(a, b, c)
|
||||
#endif
|
||||
|
||||
#ifndef lll_trylock_elision
|
||||
#define lll_trylock_elision(a,t) lll_trylock(a)
|
||||
#endif
|
||||
@@ -649,3 +654,635 @@ __pthread_mutex_timedlock (pthread_mutex_t *mutex,
|
||||
return result;
|
||||
}
|
||||
weak_alias (__pthread_mutex_timedlock, pthread_mutex_timedlock)
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__pthread_mutex_timedlock64 (pthread_mutex_t *mutex,
|
||||
const struct __timespec64 *abstime)
|
||||
{
|
||||
/* Only compile this function if kernel provides clock_gettime64 */
|
||||
#ifdef __NR_clock_gettime64
|
||||
int oldval;
|
||||
pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
|
||||
int result = 0;
|
||||
#endif
|
||||
struct timespec abstime32;
|
||||
|
||||
LIBC_PROBE (mutex_timedlock_entry, 2, mutex, abstime);
|
||||
|
||||
/* Only compile this function if kernel provides clock_gettime64 */
|
||||
#ifdef __NR_clock_gettime64
|
||||
if (__y2038_get_kernel_support())
|
||||
{
|
||||
|
||||
/* We must not check ABSTIME here. If the thread does not block
|
||||
abstime must not be checked for a valid value. */
|
||||
|
||||
switch (__builtin_expect (PTHREAD_MUTEX_TYPE_ELISION (mutex),
|
||||
PTHREAD_MUTEX_TIMED_NP))
|
||||
{
|
||||
/* Recursive mutex. */
|
||||
case PTHREAD_MUTEX_RECURSIVE_NP|PTHREAD_MUTEX_ELISION_NP:
|
||||
case PTHREAD_MUTEX_RECURSIVE_NP:
|
||||
/* Check whether we already hold the mutex. */
|
||||
if (mutex->__data.__owner == id)
|
||||
{
|
||||
/* Just bump the counter. */
|
||||
if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
|
||||
/* Overflow of the counter. */
|
||||
return EAGAIN;
|
||||
|
||||
++mutex->__data.__count;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We have to get the mutex. */
|
||||
result = lll_timedlock64 (mutex->__data.__lock, abstime,
|
||||
PTHREAD_MUTEX_PSHARED (mutex));
|
||||
|
||||
if (result != 0)
|
||||
goto out;
|
||||
|
||||
/* Only locked once so far. */
|
||||
mutex->__data.__count = 1;
|
||||
break;
|
||||
|
||||
/* Error checking mutex. */
|
||||
case PTHREAD_MUTEX_ERRORCHECK_NP:
|
||||
/* Check whether we already hold the mutex. */
|
||||
if (__glibc_unlikely (mutex->__data.__owner == id))
|
||||
return EDEADLK;
|
||||
|
||||
/* Don't do lock elision on an error checking mutex. */
|
||||
goto simple;
|
||||
|
||||
case PTHREAD_MUTEX_TIMED_NP:
|
||||
FORCE_ELISION (mutex, goto elision);
|
||||
simple:
|
||||
/* Normal mutex. */
|
||||
result = lll_timedlock64 (mutex->__data.__lock, abstime,
|
||||
PTHREAD_MUTEX_PSHARED (mutex));
|
||||
break;
|
||||
|
||||
case PTHREAD_MUTEX_TIMED_ELISION_NP:
|
||||
elision: __attribute__((unused))
|
||||
/* Don't record ownership */
|
||||
return lll_timedlock_elision64 (mutex->__data.__lock,
|
||||
mutex->__data.__spins,
|
||||
abstime,
|
||||
PTHREAD_MUTEX_PSHARED (mutex));
|
||||
|
||||
|
||||
case PTHREAD_MUTEX_ADAPTIVE_NP:
|
||||
if (! __is_smp)
|
||||
goto simple;
|
||||
|
||||
if (lll_trylock (mutex->__data.__lock) != 0)
|
||||
{
|
||||
int cnt = 0;
|
||||
int max_cnt = MIN (MAX_ADAPTIVE_COUNT,
|
||||
mutex->__data.__spins * 2 + 10);
|
||||
do
|
||||
{
|
||||
if (cnt++ >= max_cnt)
|
||||
{
|
||||
result = lll_timedlock64 (mutex->__data.__lock, abstime,
|
||||
PTHREAD_MUTEX_PSHARED (mutex));
|
||||
break;
|
||||
}
|
||||
atomic_spin_nop ();
|
||||
}
|
||||
while (lll_trylock (mutex->__data.__lock) != 0);
|
||||
|
||||
mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;
|
||||
}
|
||||
break;
|
||||
|
||||
case PTHREAD_MUTEX_ROBUST_RECURSIVE_NP:
|
||||
case PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP:
|
||||
case PTHREAD_MUTEX_ROBUST_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
&mutex->__data.__list.__next);
|
||||
/* We need to set op_pending before starting the operation. Also
|
||||
see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
/* This is set to FUTEX_WAITERS iff we might have shared the
|
||||
FUTEX_WAITERS flag with other threads, and therefore need to keep it
|
||||
set to avoid lost wake-ups. We have the same requirement in the
|
||||
simple mutex algorithm. */
|
||||
unsigned int assume_other_futex_waiters = 0;
|
||||
while (1)
|
||||
{
|
||||
/* Try to acquire the lock through a CAS from 0 (not acquired) to
|
||||
our TID | assume_other_futex_waiters. */
|
||||
if (__glibc_likely (oldval == 0))
|
||||
{
|
||||
oldval
|
||||
= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
|
||||
id | assume_other_futex_waiters, 0);
|
||||
if (__glibc_likely (oldval == 0))
|
||||
break;
|
||||
}
|
||||
|
||||
if ((oldval & FUTEX_OWNER_DIED) != 0)
|
||||
{
|
||||
/* The previous owner died. Try locking the mutex. */
|
||||
int newval = id | (oldval & FUTEX_WAITERS)
|
||||
| assume_other_futex_waiters;
|
||||
|
||||
newval
|
||||
= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
|
||||
newval, oldval);
|
||||
if (newval != oldval)
|
||||
{
|
||||
oldval = newval;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We got the mutex. */
|
||||
mutex->__data.__count = 1;
|
||||
/* But it is inconsistent unless marked otherwise. */
|
||||
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
|
||||
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Note that we deliberately exit here. If we fall
|
||||
through to the end of the function __nusers would be
|
||||
incremented which is not correct because the old
|
||||
owner has to be discounted. */
|
||||
return EOWNERDEAD;
|
||||
}
|
||||
|
||||
/* Check whether we already hold the mutex. */
|
||||
if (__glibc_unlikely ((oldval & FUTEX_TID_MASK) == id))
|
||||
{
|
||||
int kind = PTHREAD_MUTEX_TYPE (mutex);
|
||||
if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. Also see comments at ENQUEUE_MUTEX. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
NULL);
|
||||
return EDEADLK;
|
||||
}
|
||||
|
||||
if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
NULL);
|
||||
|
||||
/* Just bump the counter. */
|
||||
if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
|
||||
/* Overflow of the counter. */
|
||||
return EAGAIN;
|
||||
|
||||
++mutex->__data.__count;
|
||||
|
||||
LIBC_PROBE (mutex_timedlock_acquired, 1, mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* We are about to block; check whether the timeout is invalid. */
|
||||
if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
||||
return EINVAL;
|
||||
/* Work around the fact that the kernel rejects negative timeout
|
||||
values despite them being valid. */
|
||||
if (__glibc_unlikely (abstime->tv_sec < 0))
|
||||
return ETIMEDOUT;
|
||||
#if (!defined __ASSUME_FUTEX_CLOCK_REALTIME \
|
||||
|| !defined lll_futex_timed_wait_bitset)
|
||||
struct timeval tv;
|
||||
struct timespec rt;
|
||||
|
||||
/* Get the current time. */
|
||||
(void) __gettimeofday (&tv, NULL);
|
||||
|
||||
/* Compute relative timeout. */
|
||||
rt.tv_sec = abstime->tv_sec - tv.tv_sec;
|
||||
rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
|
||||
if (rt.tv_nsec < 0)
|
||||
{
|
||||
rt.tv_nsec += 1000000000;
|
||||
--rt.tv_sec;
|
||||
}
|
||||
|
||||
/* Already timed out? */
|
||||
if (rt.tv_sec < 0)
|
||||
return ETIMEDOUT;
|
||||
#endif
|
||||
|
||||
/* We cannot acquire the mutex nor has its owner died. Thus, try
|
||||
to block using futexes. Set FUTEX_WAITERS if necessary so that
|
||||
other threads are aware that there are potentially threads
|
||||
blocked on the futex. Restart if oldval changed in the
|
||||
meantime. */
|
||||
if ((oldval & FUTEX_WAITERS) == 0)
|
||||
{
|
||||
if (atomic_compare_and_exchange_bool_acq (&mutex->__data.__lock,
|
||||
oldval | FUTEX_WAITERS,
|
||||
oldval)
|
||||
!= 0)
|
||||
{
|
||||
oldval = mutex->__data.__lock;
|
||||
continue;
|
||||
}
|
||||
oldval |= FUTEX_WAITERS;
|
||||
}
|
||||
|
||||
/* It is now possible that we share the FUTEX_WAITERS flag with
|
||||
another thread; therefore, update assume_other_futex_waiters so
|
||||
that we do not forget about this when handling other cases
|
||||
above and thus do not cause lost wake-ups. */
|
||||
assume_other_futex_waiters |= FUTEX_WAITERS;
|
||||
|
||||
/* Block using the futex. */
|
||||
#if (!defined __ASSUME_FUTEX_CLOCK_REALTIME \
|
||||
|| !defined lll_futex_timed_wait_bitset)
|
||||
lll_futex_timed wait_64 (&mutex->__data.__lock, oldval,
|
||||
&rt, PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
|
||||
#else
|
||||
int err = lll_futex_timed_wait_bitset64 (&mutex->__data.__lock,
|
||||
oldval, abstime, FUTEX_CLOCK_REALTIME,
|
||||
PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
|
||||
/* The futex call timed out. */
|
||||
if (err == -ETIMEDOUT)
|
||||
return -err;
|
||||
#endif
|
||||
/* Reload current lock value. */
|
||||
oldval = mutex->__data.__lock;
|
||||
}
|
||||
|
||||
/* We have acquired the mutex; check if it is still consistent. */
|
||||
if (__builtin_expect (mutex->__data.__owner
|
||||
== PTHREAD_MUTEX_NOTRECOVERABLE, 0))
|
||||
{
|
||||
/* This mutex is now not recoverable. */
|
||||
mutex->__data.__count = 0;
|
||||
int private = PTHREAD_ROBUST_MUTEX_PSHARED (mutex);
|
||||
lll_unlock (mutex->__data.__lock, private);
|
||||
/* FIXME This violates the mutex destruction requirements. See
|
||||
__pthread_mutex_unlock_full. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return ENOTRECOVERABLE;
|
||||
}
|
||||
|
||||
mutex->__data.__count = 1;
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
break;
|
||||
|
||||
/* The PI support requires the Linux futex system call. If that's not
|
||||
available, pthread_mutex_init should never have allowed the type to
|
||||
be set. So it will get the default case for an invalid type. */
|
||||
#ifdef __NR_futex
|
||||
case PTHREAD_MUTEX_PI_RECURSIVE_NP:
|
||||
case PTHREAD_MUTEX_PI_ERRORCHECK_NP:
|
||||
case PTHREAD_MUTEX_PI_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PI_ADAPTIVE_NP:
|
||||
case PTHREAD_MUTEX_PI_ROBUST_RECURSIVE_NP:
|
||||
case PTHREAD_MUTEX_PI_ROBUST_ERRORCHECK_NP:
|
||||
case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP:
|
||||
{
|
||||
int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
int robust = mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
|
||||
if (robust)
|
||||
{
|
||||
/* Note: robust PI futexes are signaled by setting bit 0. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
(void *) (((uintptr_t) &mutex->__data.__list.__next)
|
||||
| 1));
|
||||
/* We need to set op_pending before starting the operation. Also
|
||||
see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
}
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
|
||||
/* Check whether we already hold the mutex. */
|
||||
if (__glibc_unlikely ((oldval & FUTEX_TID_MASK) == id))
|
||||
{
|
||||
if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return EDEADLK;
|
||||
}
|
||||
|
||||
if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Just bump the counter. */
|
||||
if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
|
||||
/* Overflow of the counter. */
|
||||
return EAGAIN;
|
||||
|
||||
++mutex->__data.__count;
|
||||
|
||||
LIBC_PROBE (mutex_timedlock_acquired, 1, mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
oldval = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
|
||||
id, 0);
|
||||
|
||||
if (oldval != 0)
|
||||
{
|
||||
/* The mutex is locked. The kernel will now take care of
|
||||
everything. The timeout value must be a relative value.
|
||||
Convert it. */
|
||||
int private = (robust
|
||||
? PTHREAD_ROBUST_MUTEX_PSHARED (mutex)
|
||||
: PTHREAD_MUTEX_PSHARED (mutex));
|
||||
INTERNAL_SYSCALL_DECL (__err);
|
||||
|
||||
int e;
|
||||
|
||||
if (abstime->tv_sec > INT_MAX)
|
||||
{
|
||||
e = EOVERFLOW;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = abstime->tv_sec;
|
||||
ts.tv_nsec = abstime->tv_nsec;
|
||||
e = INTERNAL_SYSCALL (futex, __err, 4, &mutex->__data.__lock,
|
||||
__lll_private_flag (FUTEX_LOCK_PI,
|
||||
private), 1,
|
||||
&ts);
|
||||
}
|
||||
if (INTERNAL_SYSCALL_ERROR_P (e, __err))
|
||||
{
|
||||
if (INTERNAL_SYSCALL_ERRNO (e, __err) == ETIMEDOUT)
|
||||
return ETIMEDOUT;
|
||||
|
||||
if (INTERNAL_SYSCALL_ERRNO (e, __err) == ESRCH
|
||||
|| INTERNAL_SYSCALL_ERRNO (e, __err) == EDEADLK)
|
||||
{
|
||||
assert (INTERNAL_SYSCALL_ERRNO (e, __err) != EDEADLK
|
||||
|| (kind != PTHREAD_MUTEX_ERRORCHECK_NP
|
||||
&& kind != PTHREAD_MUTEX_RECURSIVE_NP));
|
||||
/* ESRCH can happen only for non-robust PI mutexes where
|
||||
the owner of the lock died. */
|
||||
assert (INTERNAL_SYSCALL_ERRNO (e, __err) != ESRCH
|
||||
|| !robust);
|
||||
|
||||
/* Delay the thread until the timeout is reached.
|
||||
Then return ETIMEDOUT. */
|
||||
struct timespec reltime;
|
||||
struct __timespec64 now;
|
||||
|
||||
INTERNAL_SYSCALL (clock_gettime64, __err, 2, CLOCK_REALTIME,
|
||||
&now);
|
||||
reltime.tv_sec = abstime->tv_sec - now.tv_sec;
|
||||
reltime.tv_nsec = abstime->tv_nsec - now.tv_nsec;
|
||||
if (reltime.tv_nsec < 0)
|
||||
{
|
||||
reltime.tv_nsec += 1000000000;
|
||||
--reltime.tv_sec;
|
||||
}
|
||||
if (reltime.tv_sec >= 0)
|
||||
while (__nanosleep_nocancel (&reltime, &reltime) != 0)
|
||||
continue;
|
||||
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
|
||||
return INTERNAL_SYSCALL_ERRNO (e, __err);
|
||||
}
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
|
||||
assert (robust || (oldval & FUTEX_OWNER_DIED) == 0);
|
||||
}
|
||||
|
||||
if (__glibc_unlikely (oldval & FUTEX_OWNER_DIED))
|
||||
{
|
||||
atomic_and (&mutex->__data.__lock, ~FUTEX_OWNER_DIED);
|
||||
|
||||
/* We got the mutex. */
|
||||
mutex->__data.__count = 1;
|
||||
/* But it is inconsistent unless marked otherwise. */
|
||||
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
|
||||
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX_PI (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Note that we deliberately exit here. If we fall
|
||||
through to the end of the function __nusers would be
|
||||
incremented which is not correct because the old owner
|
||||
has to be discounted. */
|
||||
return EOWNERDEAD;
|
||||
}
|
||||
|
||||
if (robust
|
||||
&& __builtin_expect (mutex->__data.__owner
|
||||
== PTHREAD_MUTEX_NOTRECOVERABLE, 0))
|
||||
{
|
||||
/* This mutex is now not recoverable. */
|
||||
mutex->__data.__count = 0;
|
||||
|
||||
INTERNAL_SYSCALL_DECL (__err);
|
||||
INTERNAL_SYSCALL (futex, __err, 4, &mutex->__data.__lock,
|
||||
__lll_private_flag (FUTEX_UNLOCK_PI,
|
||||
PTHREAD_ROBUST_MUTEX_PSHARED (mutex)),
|
||||
0, 0);
|
||||
|
||||
/* To the kernel, this will be visible after the kernel has
|
||||
acquired the mutex in the syscall. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return ENOTRECOVERABLE;
|
||||
}
|
||||
|
||||
mutex->__data.__count = 1;
|
||||
if (robust)
|
||||
{
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX_PI (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif /* __NR_futex. */
|
||||
|
||||
case PTHREAD_MUTEX_PP_RECURSIVE_NP:
|
||||
case PTHREAD_MUTEX_PP_ERRORCHECK_NP:
|
||||
case PTHREAD_MUTEX_PP_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PP_ADAPTIVE_NP:
|
||||
{
|
||||
int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
|
||||
/* Check whether we already hold the mutex. */
|
||||
if (mutex->__data.__owner == id)
|
||||
{
|
||||
if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
|
||||
return EDEADLK;
|
||||
|
||||
if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
|
||||
{
|
||||
/* Just bump the counter. */
|
||||
if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
|
||||
/* Overflow of the counter. */
|
||||
return EAGAIN;
|
||||
|
||||
++mutex->__data.__count;
|
||||
|
||||
LIBC_PROBE (mutex_timedlock_acquired, 1, mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int oldprio = -1, ceilval;
|
||||
do
|
||||
{
|
||||
int ceiling = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK)
|
||||
>> PTHREAD_MUTEX_PRIO_CEILING_SHIFT;
|
||||
|
||||
if (__pthread_current_priority () > ceiling)
|
||||
{
|
||||
result = EINVAL;
|
||||
failpp:
|
||||
if (oldprio != -1)
|
||||
__pthread_tpp_change_priority (oldprio, -1);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = __pthread_tpp_change_priority (oldprio, ceiling);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
ceilval = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT;
|
||||
oldprio = ceiling;
|
||||
|
||||
oldval
|
||||
= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
|
||||
ceilval | 1, ceilval);
|
||||
|
||||
if (oldval == ceilval)
|
||||
break;
|
||||
|
||||
do
|
||||
{
|
||||
oldval
|
||||
= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
|
||||
ceilval | 2,
|
||||
ceilval | 1);
|
||||
|
||||
if ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval)
|
||||
break;
|
||||
|
||||
if (oldval != ceilval)
|
||||
{
|
||||
/* Reject invalid timeouts. */
|
||||
if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
||||
{
|
||||
result = EINVAL;
|
||||
goto failpp;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
struct timespec rt;
|
||||
|
||||
/* Get the current time. */
|
||||
(void) __gettimeofday (&tv, NULL);
|
||||
|
||||
/* Compute relative timeout. */
|
||||
rt.tv_sec = abstime->tv_sec - tv.tv_sec;
|
||||
rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
|
||||
if (rt.tv_nsec < 0)
|
||||
{
|
||||
rt.tv_nsec += 1000000000;
|
||||
--rt.tv_sec;
|
||||
}
|
||||
|
||||
/* Already timed out? */
|
||||
if (rt.tv_sec < 0)
|
||||
{
|
||||
result = ETIMEDOUT;
|
||||
goto failpp;
|
||||
}
|
||||
|
||||
lll_futex_timed_wait (&mutex->__data.__lock,
|
||||
ceilval | 2, &rt,
|
||||
PTHREAD_MUTEX_PSHARED (mutex));
|
||||
}
|
||||
}
|
||||
while (atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
|
||||
ceilval | 2, ceilval)
|
||||
!= ceilval);
|
||||
}
|
||||
while ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval);
|
||||
|
||||
assert (mutex->__data.__owner == 0);
|
||||
mutex->__data.__count = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Correct code cannot set any other type. */
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
/* Record the ownership. */
|
||||
mutex->__data.__owner = id;
|
||||
++mutex->__data.__nusers;
|
||||
|
||||
LIBC_PROBE (mutex_timedlock_acquired, 1, mutex);
|
||||
}
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (abstime->tv_sec > INT_MAX || abstime->tv_sec < INT_MIN)
|
||||
{
|
||||
return EOVERFLOW;
|
||||
}
|
||||
|
||||
abstime32.tv_sec = (time_t) (abstime->tv_sec);
|
||||
abstime32.tv_nsec = abstime->tv_nsec;
|
||||
|
||||
return __pthread_mutex_timedlock (mutex, &abstime32);
|
||||
}
|
||||
|
||||
@@ -507,6 +507,240 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
static __always_inline int
|
||||
__pthread_rwlock_rdlock_full64 (pthread_rwlock_t *rwlock,
|
||||
const struct __timespec64 *abstime)
|
||||
{
|
||||
unsigned int r;
|
||||
|
||||
/* Make sure we are not holding the rwlock as a writer. This is a deadlock
|
||||
situation we recognize and report. */
|
||||
if (__glibc_unlikely (atomic_load_relaxed (&rwlock->__data.__cur_writer)
|
||||
== THREAD_GETMEM (THREAD_SELF, tid)))
|
||||
return EDEADLK;
|
||||
|
||||
/* If we prefer writers, recursive rdlock is disallowed, we are in a read
|
||||
phase, and there are other readers present, we try to wait without
|
||||
extending the read phase. We will be unblocked by either one of the
|
||||
other active readers, or if the writer gives up WRLOCKED (e.g., on
|
||||
timeout).
|
||||
If there are no other readers, we simply race with any existing primary
|
||||
writer; it would have been a race anyway, and changing the odds slightly
|
||||
will likely not make a big difference. */
|
||||
if (rwlock->__data.__flags == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)
|
||||
{
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
while (((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
&& ((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
|
||||
&& ((r >> PTHREAD_RWLOCK_READER_SHIFT) > 0))
|
||||
{
|
||||
/* TODO Spin first. */
|
||||
/* Try setting the flag signaling that we are waiting without having
|
||||
incremented the number of readers. Relaxed MO is fine because
|
||||
this is just about waiting for a state change in __readers. */
|
||||
if (atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__readers, &r, r | PTHREAD_RWLOCK_RWAITING))
|
||||
{
|
||||
/* Wait for as long as the flag is set. An ABA situation is
|
||||
harmless because the flag is just about the state of
|
||||
__readers, and all threads set the flag under the same
|
||||
conditions. */
|
||||
while ((atomic_load_relaxed (&rwlock->__data.__readers)
|
||||
& PTHREAD_RWLOCK_RWAITING) != 0)
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
int err = futex_abstimed_wait64 (&rwlock->__data.__readers,
|
||||
r, abstime, private);
|
||||
/* We ignore EAGAIN and EINTR. On time-outs, we can just
|
||||
return because we don't need to clean up anything. */
|
||||
if (err == ETIMEDOUT)
|
||||
return err;
|
||||
}
|
||||
/* It makes sense to not break out of the outer loop here
|
||||
because we might be in the same situation again. */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Register as a reader, using an add-and-fetch so that R can be used as
|
||||
expected value for future operations. Acquire MO so we synchronize with
|
||||
prior writers as well as the last reader of the previous read phase (see
|
||||
below). */
|
||||
r = atomic_fetch_add_acquire (&rwlock->__data.__readers,
|
||||
(1 << PTHREAD_RWLOCK_READER_SHIFT)) + (1 << PTHREAD_RWLOCK_READER_SHIFT);
|
||||
|
||||
/* Check whether there is an overflow in the number of readers. We assume
|
||||
that the total number of threads is less than half the maximum number
|
||||
of readers that we have bits for in __readers (i.e., with 32-bit int and
|
||||
PTHREAD_RWLOCK_READER_SHIFT of 3, we assume there are less than
|
||||
1 << (32-3-1) concurrent threads).
|
||||
If there is an overflow, we use a CAS to try to decrement the number of
|
||||
readers if there still is an overflow situation. If so, we return
|
||||
EAGAIN; if not, we are not a thread causing an overflow situation, and so
|
||||
we just continue. Using a fetch-add instead of the CAS isn't possible
|
||||
because other readers might release the lock concurrently, which could
|
||||
make us the last reader and thus responsible for handing ownership over
|
||||
to writers (which requires a CAS too to make the decrement and ownership
|
||||
transfer indivisible). */
|
||||
while (__glibc_unlikely (r >= PTHREAD_RWLOCK_READER_OVERFLOW))
|
||||
{
|
||||
/* Relaxed MO is okay because we just want to undo our registration and
|
||||
cannot have changed the rwlock state substantially if the CAS
|
||||
succeeds. */
|
||||
if (atomic_compare_exchange_weak_relaxed (&rwlock->__data.__readers, &r,
|
||||
r - (1 << PTHREAD_RWLOCK_READER_SHIFT)))
|
||||
return EAGAIN;
|
||||
}
|
||||
|
||||
/* We have registered as a reader, so if we are in a read phase, we have
|
||||
acquired a read lock. This is also the reader--reader fast-path.
|
||||
Even if there is a primary writer, we just return. If writers are to
|
||||
be preferred and we are the only active reader, we could try to enter a
|
||||
write phase to let the writer proceed. This would be okay because we
|
||||
cannot have acquired the lock previously as a reader (which could result
|
||||
in deadlock if we would wait for the primary writer to run). However,
|
||||
this seems to be a corner case and handling it specially not be worth the
|
||||
complexity. */
|
||||
if (__glibc_likely ((r & PTHREAD_RWLOCK_WRPHASE) == 0))
|
||||
return 0;
|
||||
|
||||
/* If there is no primary writer but we are in a write phase, we can try
|
||||
to install a read phase ourself. */
|
||||
while (((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
&& ((r & PTHREAD_RWLOCK_WRLOCKED) == 0))
|
||||
{
|
||||
/* Try to enter a read phase: If the CAS below succeeds, we have
|
||||
ownership; if it fails, we will simply retry and reassess the
|
||||
situation.
|
||||
Acquire MO so we synchronize with prior writers. */
|
||||
if (atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, &r,
|
||||
r ^ PTHREAD_RWLOCK_WRPHASE))
|
||||
{
|
||||
/* We started the read phase, so we are also responsible for
|
||||
updating the write-phase futex. Relaxed MO is sufficient.
|
||||
Note that there can be no other reader that we have to wake
|
||||
because all other readers will see the read phase started by us
|
||||
(or they will try to start it themselves); if a writer started
|
||||
the read phase, we cannot have started it. Furthermore, we
|
||||
cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
|
||||
overwrite the value set by the most recent writer (or the readers
|
||||
before it in case of explicit hand-over) and we know that there
|
||||
are no waiting readers. */
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO Back off before retrying. Also see above. */
|
||||
}
|
||||
}
|
||||
|
||||
if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
{
|
||||
/* We are in a write phase, and there must be a primary writer because
|
||||
of the previous loop. Block until the primary writer gives up the
|
||||
write phase. This case requires explicit hand-over using
|
||||
__wrphase_futex.
|
||||
However, __wrphase_futex might not have been set to 1 yet (either
|
||||
because explicit hand-over to the writer is still ongoing, or because
|
||||
the writer has started the write phase but does not yet have updated
|
||||
__wrphase_futex). The least recent value of __wrphase_futex we can
|
||||
read from here is the modification of the last read phase (because
|
||||
we synchronize with the last reader in this read phase through
|
||||
__readers; see the use of acquire MO on the fetch_add above).
|
||||
Therefore, if we observe a value of 0 for __wrphase_futex, we need
|
||||
to subsequently check that __readers now indicates a read phase; we
|
||||
need to use acquire MO for this so that if we observe a read phase,
|
||||
we will also see the modification of __wrphase_futex by the previous
|
||||
writer. We then need to load __wrphase_futex again and continue to
|
||||
wait if it is not 0, so that we do not skip explicit hand-over.
|
||||
Relaxed MO is sufficient for the load from __wrphase_futex because
|
||||
we just use it as an indicator for when we can proceed; we use
|
||||
__readers and the acquire MO accesses to it to eventually read from
|
||||
the proper stores to __wrphase_futex. */
|
||||
unsigned int wpf;
|
||||
bool ready = false;
|
||||
for (;;)
|
||||
{
|
||||
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
| PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||
&& !atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__wrphase_futex,
|
||||
&wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
continue;
|
||||
int err = futex_abstimed_wait64 (&rwlock->__data.__wrphase_futex,
|
||||
1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
/* If we timed out, we need to unregister. If no read phase
|
||||
has been installed while we waited, we can just decrement
|
||||
the number of readers. Otherwise, we just acquire the
|
||||
lock, which is allowed because we give no precise timing
|
||||
guarantees, and because the timeout is only required to
|
||||
be in effect if we would have had to wait for other
|
||||
threads (e.g., if futex_wait would time-out immediately
|
||||
because the given absolute time is in the past). */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
while ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
{
|
||||
/* We don't need to make anything else visible to
|
||||
others besides unregistering, so relaxed MO is
|
||||
sufficient. */
|
||||
if (atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__readers, &r,
|
||||
r - (1 << PTHREAD_RWLOCK_READER_SHIFT)))
|
||||
return ETIMEDOUT;
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
/* Use the acquire MO fence to mirror the steps taken in the
|
||||
non-timeout case. Note that the read can happen both
|
||||
in the atomic_load above as well as in the failure case
|
||||
of the CAS operation. */
|
||||
atomic_thread_fence_acquire ();
|
||||
/* We still need to wait for explicit hand-over, but we must
|
||||
not use futex_wait anymore because we would just time out
|
||||
in this case and thus make the spin-waiting we need
|
||||
unnecessarily expensive. */
|
||||
while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex)
|
||||
| PTHREAD_RWLOCK_FUTEX_USED)
|
||||
== (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
{
|
||||
/* TODO Back-off? */
|
||||
}
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
/* If we got interrupted (EINTR) or the futex word does not have the
|
||||
expected value (EAGAIN), retry. */
|
||||
}
|
||||
if (ready)
|
||||
/* See below. */
|
||||
break;
|
||||
/* We need acquire MO here so that we synchronize with the lock
|
||||
release of the writer, and so that we observe a recent value of
|
||||
__wrphase_futex (see below). */
|
||||
if ((atomic_load_acquire (&rwlock->__data.__readers)
|
||||
& PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
/* We are in a read phase now, so the least recent modification of
|
||||
__wrphase_futex we can read from is the store by the writer
|
||||
with value 1. Thus, only now we can assume that if we observe
|
||||
a value of 0, explicit hand-over is finished. Retry the loop
|
||||
above one more time. */
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static __always_inline void
|
||||
__pthread_rwlock_wrunlock (pthread_rwlock_t *rwlock)
|
||||
@@ -924,3 +1158,360 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock,
|
||||
THREAD_GETMEM (THREAD_SELF, tid));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
static __always_inline int
|
||||
__pthread_rwlock_wrlock_full64 (pthread_rwlock_t *rwlock,
|
||||
const struct __timespec64 *abstime)
|
||||
{
|
||||
/* Make sure we are not holding the rwlock as a writer. This is a deadlock
|
||||
situation we recognize and report. */
|
||||
if (__glibc_unlikely (atomic_load_relaxed (&rwlock->__data.__cur_writer)
|
||||
== THREAD_GETMEM (THREAD_SELF, tid)))
|
||||
return EDEADLK;
|
||||
|
||||
/* First we try to acquire the role of primary writer by setting WRLOCKED;
|
||||
if it was set before, there already is a primary writer. Acquire MO so
|
||||
that we synchronize with previous primary writers.
|
||||
|
||||
We do not try to change to a write phase right away using a fetch_or
|
||||
because we would have to reset it again and wake readers if there are
|
||||
readers present (some readers could try to acquire the lock more than
|
||||
once, so setting a write phase in the middle of this could cause
|
||||
deadlock). Changing to a write phase eagerly would only speed up the
|
||||
transition from a read phase to a write phase in the uncontended case,
|
||||
but it would slow down the contended case if readers are preferred (which
|
||||
is the default).
|
||||
We could try to CAS from a state with no readers to a write phase, but
|
||||
this could be less scalable if readers arrive and leave frequently. */
|
||||
bool may_share_futex_used_flag = false;
|
||||
unsigned int r = atomic_fetch_or_acquire (&rwlock->__data.__readers,
|
||||
PTHREAD_RWLOCK_WRLOCKED);
|
||||
if (__glibc_unlikely ((r & PTHREAD_RWLOCK_WRLOCKED) != 0))
|
||||
{
|
||||
/* There is another primary writer. */
|
||||
bool prefer_writer =
|
||||
(rwlock->__data.__flags != PTHREAD_RWLOCK_PREFER_READER_NP);
|
||||
if (prefer_writer)
|
||||
{
|
||||
/* We register as a waiting writer, so that we can make use of
|
||||
writer--writer hand-over. Relaxed MO is fine because we just
|
||||
want to register. We assume that the maximum number of threads
|
||||
is less than the capacity in __writers. */
|
||||
atomic_fetch_add_relaxed (&rwlock->__data.__writers, 1);
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
/* TODO Spin until WRLOCKED is 0 before trying the CAS below.
|
||||
But pay attention to not delay trying writer--writer hand-over
|
||||
for too long (which we must try eventually anyway). */
|
||||
if ((r & PTHREAD_RWLOCK_WRLOCKED) == 0)
|
||||
{
|
||||
/* Try to become the primary writer or retry. Acquire MO as in
|
||||
the fetch_or above. */
|
||||
if (atomic_compare_exchange_weak_acquire
|
||||
(&rwlock->__data.__readers, &r,
|
||||
r | PTHREAD_RWLOCK_WRLOCKED))
|
||||
{
|
||||
if (prefer_writer)
|
||||
{
|
||||
/* Unregister as a waiting writer. Note that because we
|
||||
acquired WRLOCKED, WRHANDOVER will not be set.
|
||||
Acquire MO on the CAS above ensures that
|
||||
unregistering happens after the previous writer;
|
||||
this sorts the accesses to __writers by all
|
||||
primary writers in a useful way (e.g., any other
|
||||
primary writer acquiring after us or getting it from
|
||||
us through WRHANDOVER will see both our changes to
|
||||
__writers).
|
||||
??? Perhaps this is not strictly necessary for
|
||||
reasons we do not yet know of. */
|
||||
atomic_fetch_add_relaxed (&rwlock->__data.__writers,
|
||||
-1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Retry if the CAS fails (r will have been updated). */
|
||||
continue;
|
||||
}
|
||||
/* If writer--writer hand-over is available, try to become the
|
||||
primary writer this way by grabbing the WRHANDOVER token. If we
|
||||
succeed, we own WRLOCKED. */
|
||||
if (prefer_writer)
|
||||
{
|
||||
unsigned int w = atomic_load_relaxed
|
||||
(&rwlock->__data.__writers);
|
||||
if ((w & PTHREAD_RWLOCK_WRHANDOVER) != 0)
|
||||
{
|
||||
/* Acquire MO is required here so that we synchronize with
|
||||
the writer that handed over WRLOCKED. We also need this
|
||||
for the reload of __readers below because our view of
|
||||
__readers must be at least as recent as the view of the
|
||||
writer that handed over WRLOCKED; we must avoid an ABA
|
||||
through WRHANDOVER, which could, for example, lead to us
|
||||
assuming we are still in a write phase when in fact we
|
||||
are not. */
|
||||
if (atomic_compare_exchange_weak_acquire
|
||||
(&rwlock->__data.__writers,
|
||||
&w, (w - PTHREAD_RWLOCK_WRHANDOVER - 1)))
|
||||
{
|
||||
/* Reload so our view is consistent with the view of
|
||||
the previous owner of WRLOCKED. See above. */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
break;
|
||||
}
|
||||
/* We do not need to reload __readers here. We should try
|
||||
to perform writer--writer hand-over if possible; if it
|
||||
is not possible anymore, we will reload __readers
|
||||
elsewhere in this loop. */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* We did not acquire WRLOCKED nor were able to use writer--writer
|
||||
hand-over, so we block on __writers_futex. */
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
unsigned int wf = atomic_load_relaxed
|
||||
(&rwlock->__data.__writers_futex);
|
||||
if (((wf & ~(unsigned int) PTHREAD_RWLOCK_FUTEX_USED) != 1)
|
||||
|| ((wf != (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
&& !atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__writers_futex, &wf,
|
||||
1 | PTHREAD_RWLOCK_FUTEX_USED)))
|
||||
{
|
||||
/* If we cannot block on __writers_futex because there is no
|
||||
primary writer, or we cannot set PTHREAD_RWLOCK_FUTEX_USED,
|
||||
we retry. We must reload __readers here in case we cannot
|
||||
block on __writers_futex so that we can become the primary
|
||||
writer and are not stuck in a loop that just continuously
|
||||
fails to block on __writers_futex. */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
continue;
|
||||
}
|
||||
/* We set the flag that signals that the futex is used, or we could
|
||||
have set it if we had been faster than other waiters. As a
|
||||
result, we may share the flag with an unknown number of other
|
||||
writers. Therefore, we must keep this flag set when we acquire
|
||||
the lock. We do not need to do this when we do not reach this
|
||||
point here because then we are not part of the group that may
|
||||
share the flag, and another writer will wake one of the writers
|
||||
in this group. */
|
||||
may_share_futex_used_flag = true;
|
||||
int err = futex_abstimed_wait64 (&rwlock->__data.__writers_futex,
|
||||
1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
if (prefer_writer)
|
||||
{
|
||||
/* We need to unregister as a waiting writer. If we are the
|
||||
last writer and writer--writer hand-over is available,
|
||||
we must make use of it because nobody else will reset
|
||||
WRLOCKED otherwise. (If we use it, we simply pretend
|
||||
that this happened before the timeout; see
|
||||
pthread_rwlock_rdlock_full for the full reasoning.)
|
||||
Also see the similar code above. */
|
||||
unsigned int w = atomic_load_relaxed
|
||||
(&rwlock->__data.__writers);
|
||||
while (!atomic_compare_exchange_weak_acquire
|
||||
(&rwlock->__data.__writers, &w,
|
||||
(w == PTHREAD_RWLOCK_WRHANDOVER + 1 ? 0 : w - 1)))
|
||||
{
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
if (w == PTHREAD_RWLOCK_WRHANDOVER + 1)
|
||||
{
|
||||
/* We must continue as primary writer. See above. */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* We cleaned up and cannot have stolen another waiting writer's
|
||||
futex wake-up, so just return. */
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
/* If we got interrupted (EINTR) or the futex word does not have the
|
||||
expected value (EAGAIN), retry after reloading __readers. */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
}
|
||||
/* Our snapshot of __readers is up-to-date at this point because we
|
||||
either set WRLOCKED using a CAS or were handed over WRLOCKED from
|
||||
another writer whose snapshot of __readers we inherit. */
|
||||
}
|
||||
|
||||
/* If we are in a read phase and there are no readers, try to start a write
|
||||
phase. */
|
||||
while (((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
&& ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0))
|
||||
{
|
||||
/* Acquire MO so that we synchronize with prior writers and do
|
||||
not interfere with their updates to __writers_futex, as well
|
||||
as regarding prior readers and their updates to __wrphase_futex,
|
||||
respectively. */
|
||||
if (atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers,
|
||||
&r, r | PTHREAD_RWLOCK_WRPHASE))
|
||||
{
|
||||
/* We have started a write phase, so need to enable readers to wait.
|
||||
See the similar case in__pthread_rwlock_rdlock_full. */
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||
/* Make sure we fall through to the end of the function. */
|
||||
r |= PTHREAD_RWLOCK_WRPHASE;
|
||||
break;
|
||||
}
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
|
||||
/* We are the primary writer; enable blocking on __writers_futex. Relaxed
|
||||
MO is sufficient for futex words; acquire MO on the previous
|
||||
modifications of __readers ensures that this store happens after the
|
||||
store of value 0 by the previous primary writer. */
|
||||
atomic_store_relaxed (&rwlock->__data.__writers_futex,
|
||||
1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0));
|
||||
|
||||
if (__glibc_unlikely ((r & PTHREAD_RWLOCK_WRPHASE) == 0))
|
||||
{
|
||||
/* We are not in a read phase and there are readers (because of the
|
||||
previous loop). Thus, we have to wait for explicit hand-over from
|
||||
one of these readers.
|
||||
We basically do the same steps as for the similar case in
|
||||
__pthread_rwlock_rdlock_full, except that we additionally might try
|
||||
to directly hand over to another writer and need to wake up
|
||||
other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING). */
|
||||
unsigned int wpf;
|
||||
bool ready = false;
|
||||
for (;;)
|
||||
{
|
||||
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
| PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED)
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||
&& !atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__wrphase_futex, &wpf,
|
||||
PTHREAD_RWLOCK_FUTEX_USED))
|
||||
continue;
|
||||
int err = futex_abstimed_wait64 (&rwlock->__data.__wrphase_futex,
|
||||
PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
if (rwlock->__data.__flags
|
||||
!= PTHREAD_RWLOCK_PREFER_READER_NP)
|
||||
{
|
||||
/* We try writer--writer hand-over. */
|
||||
unsigned int w = atomic_load_relaxed
|
||||
(&rwlock->__data.__writers);
|
||||
if (w != 0)
|
||||
{
|
||||
/* We are about to hand over WRLOCKED, so we must
|
||||
release __writers_futex too; otherwise, we'd have
|
||||
a pending store, which could at least prevent
|
||||
other threads from waiting using the futex
|
||||
because it could interleave with the stores
|
||||
by subsequent writers. In turn, this means that
|
||||
we have to clean up when we do not hand over
|
||||
WRLOCKED.
|
||||
Release MO so that another writer that gets
|
||||
WRLOCKED from us can take over our view of
|
||||
__readers. */
|
||||
unsigned int wf = atomic_exchange_relaxed
|
||||
(&rwlock->__data.__writers_futex, 0);
|
||||
while (w != 0)
|
||||
{
|
||||
if (atomic_compare_exchange_weak_release
|
||||
(&rwlock->__data.__writers, &w,
|
||||
w | PTHREAD_RWLOCK_WRHANDOVER))
|
||||
{
|
||||
/* Wake other writers. */
|
||||
if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
futex_wake
|
||||
(&rwlock->__data.__writers_futex, 1,
|
||||
private);
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
/* We still own WRLOCKED and someone else might set
|
||||
a write phase concurrently, so enable waiting
|
||||
again. Make sure we don't loose the flag that
|
||||
signals whether there are threads waiting on
|
||||
this futex. */
|
||||
atomic_store_relaxed
|
||||
(&rwlock->__data.__writers_futex, wf);
|
||||
}
|
||||
}
|
||||
/* If we timed out and we are not in a write phase, we can
|
||||
just stop being a primary writer. Otherwise, we just
|
||||
acquire the lock. */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
{
|
||||
/* We are about to release WRLOCKED, so we must release
|
||||
__writers_futex too; see the handling of
|
||||
writer--writer hand-over above. */
|
||||
unsigned int wf = atomic_exchange_relaxed
|
||||
(&rwlock->__data.__writers_futex, 0);
|
||||
while ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
{
|
||||
/* While we don't need to make anything from a
|
||||
caller's critical section visible to other
|
||||
threads, we need to ensure that our changes to
|
||||
__writers_futex are properly ordered.
|
||||
Therefore, use release MO to synchronize with
|
||||
subsequent primary writers. Also wake up any
|
||||
waiting readers as they are waiting because of
|
||||
us. */
|
||||
if (atomic_compare_exchange_weak_release
|
||||
(&rwlock->__data.__readers, &r,
|
||||
(r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||
& ~(unsigned int) PTHREAD_RWLOCK_RWAITING))
|
||||
{
|
||||
/* Wake other writers. */
|
||||
if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
futex_wake (&rwlock->__data.__writers_futex,
|
||||
1, private);
|
||||
/* Wake waiting readers. */
|
||||
if ((r & PTHREAD_RWLOCK_RWAITING) != 0)
|
||||
futex_wake (&rwlock->__data.__readers,
|
||||
INT_MAX, private);
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
/* We still own WRLOCKED and someone else might set a
|
||||
write phase concurrently, so enable waiting again.
|
||||
Make sure we don't loose the flag that signals
|
||||
whether there are threads waiting on this futex. */
|
||||
atomic_store_relaxed (&rwlock->__data.__writers_futex,
|
||||
wf);
|
||||
}
|
||||
/* Use the acquire MO fence to mirror the steps taken in the
|
||||
non-timeout case. Note that the read can happen both
|
||||
in the atomic_load above as well as in the failure case
|
||||
of the CAS operation. */
|
||||
atomic_thread_fence_acquire ();
|
||||
/* We still need to wait for explicit hand-over, but we must
|
||||
not use futex_wait anymore. */
|
||||
while ((atomic_load_relaxed
|
||||
(&rwlock->__data.__wrphase_futex)
|
||||
| PTHREAD_RWLOCK_FUTEX_USED)
|
||||
== PTHREAD_RWLOCK_FUTEX_USED)
|
||||
{
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
/* If we got interrupted (EINTR) or the futex word does not have
|
||||
the expected value (EAGAIN), retry. */
|
||||
}
|
||||
/* See pthread_rwlock_rdlock_full. */
|
||||
if (ready)
|
||||
break;
|
||||
if ((atomic_load_acquire (&rwlock->__data.__readers)
|
||||
& PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
atomic_store_relaxed (&rwlock->__data.__cur_writer,
|
||||
THREAD_GETMEM (THREAD_SELF, tid));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -35,3 +35,22 @@ pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock,
|
||||
|
||||
return __pthread_rwlock_rdlock_full (rwlock, abstime);
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__pthread_rwlock_timedrdlock64 (pthread_rwlock_t *rwlock,
|
||||
const struct __timespec64 *abstime)
|
||||
{
|
||||
/* Make sure the passed in timeout value is valid. Note that the previous
|
||||
implementation assumed that this check *must* not be performed if there
|
||||
would in fact be no blocking; however, POSIX only requires that "the
|
||||
validity of the abstime parameter need not be checked if the lock can be
|
||||
immediately acquired" (i.e., we need not but may check it). */
|
||||
/* ??? Just move this to __pthread_rwlock_rdlock_full? */
|
||||
if (__glibc_unlikely (abstime->tv_nsec >= 1000000000
|
||||
|| abstime->tv_nsec < 0))
|
||||
return EINVAL;
|
||||
|
||||
return __pthread_rwlock_rdlock_full64 (rwlock, abstime);
|
||||
}
|
||||
|
||||
@@ -35,3 +35,22 @@ pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock,
|
||||
|
||||
return __pthread_rwlock_wrlock_full (rwlock, abstime);
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__pthread_rwlock_timedwrlock64 (pthread_rwlock_t *rwlock,
|
||||
const struct __timespec64 *abstime)
|
||||
{
|
||||
/* Make sure the passed in timeout value is valid. Note that the previous
|
||||
implementation assumed that this check *must* not be performed if there
|
||||
would in fact be no blocking; however, POSIX only requires that "the
|
||||
validity of the abstime parameter need not be checked if the lock can be
|
||||
immediately acquired" (i.e., we need not but may check it). */
|
||||
/* ??? Just move this to __pthread_rwlock_wrlock_full? */
|
||||
if (__glibc_unlikely (abstime->tv_nsec >= 1000000000
|
||||
|| abstime->tv_nsec < 0))
|
||||
return EINVAL;
|
||||
|
||||
return __pthread_rwlock_wrlock_full64 (rwlock, abstime);
|
||||
}
|
||||
|
||||
@@ -38,3 +38,21 @@ sem_timedwait (sem_t *sem, const struct timespec *abstime)
|
||||
else
|
||||
return __new_sem_wait_slow((struct new_sem *) sem, abstime);
|
||||
}
|
||||
|
||||
int
|
||||
__sem_timedwait64 (sem_t *sem, const struct __timespec64 *abstime)
|
||||
{
|
||||
if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check sem_wait.c for a more detailed explanation why it is required. */
|
||||
__pthread_testcancel ();
|
||||
|
||||
if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0)
|
||||
return 0;
|
||||
else
|
||||
return __new_sem_wait_slow64 ((struct new_sem *) sem, abstime);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,30 @@ __new_sem_wait (sem_t *sem)
|
||||
}
|
||||
versioned_symbol (libpthread, __new_sem_wait, sem_wait, GLIBC_2_1);
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__new_sem_wait64 (sem_t *sem)
|
||||
{
|
||||
/* We need to check whether we need to act upon a cancellation request here
|
||||
because POSIX specifies that cancellation points "shall occur" in
|
||||
sem_wait and sem_timedwait, which also means that they need to check
|
||||
this regardless whether they block or not (unlike "may occur"
|
||||
functions). See the POSIX Rationale for this requirement: Section
|
||||
"Thread Cancellation Overview" [1] and austin group issue #1076 [2]
|
||||
for thoughs on why this may be a suboptimal design.
|
||||
|
||||
[1] http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html
|
||||
[2] http://austingroupbugs.net/view.php?id=1076 for thoughts on why this
|
||||
*/
|
||||
__pthread_testcancel ();
|
||||
|
||||
if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0)
|
||||
return 0;
|
||||
else
|
||||
return __new_sem_wait_slow64 ((struct new_sem *) sem, NULL);
|
||||
}
|
||||
|
||||
#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_1)
|
||||
int
|
||||
attribute_compat_text_section
|
||||
|
||||
@@ -119,6 +119,24 @@ do_futex_wait (struct new_sem *sem, const struct timespec *abstime)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
__attribute__ ((noinline))
|
||||
do_futex_wait64 (struct new_sem *sem, const struct __timespec64 *abstime)
|
||||
{
|
||||
int err;
|
||||
|
||||
#if __HAVE_64B_ATOMICS
|
||||
err = futex_abstimed_wait_cancelable64 (
|
||||
(unsigned int *) &sem->data + SEM_VALUE_OFFSET, 0, abstime,
|
||||
sem->private);
|
||||
#else
|
||||
err = futex_abstimed_wait_cancelable64 (&sem->value, SEM_NWAITERS_MASK,
|
||||
abstime, sem->private);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Fast path: Try to grab a token without blocking. */
|
||||
static int
|
||||
__new_sem_wait_fast (struct new_sem *sem, int definitive_result)
|
||||
@@ -310,6 +328,160 @@ error:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
static int
|
||||
__attribute__ ((noinline))
|
||||
__new_sem_wait_slow64 (struct new_sem *sem, const struct __timespec64 *abstime)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#if __HAVE_64B_ATOMICS
|
||||
/* Add a waiter. Relaxed MO is sufficient because we can rely on the
|
||||
ordering provided by the RMW operations we use. */
|
||||
uint64_t d = atomic_fetch_add_relaxed (&sem->data,
|
||||
(uint64_t) 1 << SEM_NWAITERS_SHIFT);
|
||||
|
||||
pthread_cleanup_push (__sem_wait_cleanup, sem);
|
||||
|
||||
/* Wait for a token to be available. Retry until we can grab one. */
|
||||
for (;;)
|
||||
{
|
||||
/* If there is no token available, sleep until there is. */
|
||||
if ((d & SEM_VALUE_MASK) == 0)
|
||||
{
|
||||
err = do_futex_wait64 (sem, abstime);
|
||||
/* A futex return value of 0 or EAGAIN is due to a real or spurious
|
||||
wake-up, or due to a change in the number of tokens. We retry in
|
||||
these cases.
|
||||
If we timed out, forward this to the caller.
|
||||
EINTR is returned if we are interrupted by a signal; we
|
||||
forward this to the caller. (See futex_wait and related
|
||||
documentation. Before Linux 2.6.22, EINTR was also returned on
|
||||
spurious wake-ups; we only support more recent Linux versions,
|
||||
so do not need to consider this here.) */
|
||||
if (err == ETIMEDOUT || err == EINTR)
|
||||
{
|
||||
__set_errno (err);
|
||||
err = -1;
|
||||
/* Stop being registered as a waiter. */
|
||||
atomic_fetch_add_relaxed (&sem->data,
|
||||
-((uint64_t) 1 << SEM_NWAITERS_SHIFT));
|
||||
break;
|
||||
}
|
||||
/* Relaxed MO is sufficient; see below. */
|
||||
d = atomic_load_relaxed (&sem->data);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Try to grab both a token and stop being a waiter. We need
|
||||
acquire MO so this synchronizes with all token providers (i.e.,
|
||||
the RMW operation we read from or all those before it in
|
||||
modification order; also see sem_post). On the failure path,
|
||||
relaxed MO is sufficient because we only eventually need the
|
||||
up-to-date value; the futex_wait or the CAS perform the real
|
||||
work. */
|
||||
if (atomic_compare_exchange_weak_acquire (&sem->data,
|
||||
&d, d - 1 - ((uint64_t) 1 << SEM_NWAITERS_SHIFT)))
|
||||
{
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cleanup_pop (0);
|
||||
#else
|
||||
/* The main difference to the 64b-atomics implementation is that we need to
|
||||
access value and nwaiters in separate steps, and that the nwaiters bit
|
||||
in the value can temporarily not be set even if nwaiters is nonzero.
|
||||
We work around incorrectly unsetting the nwaiters bit by letting sem_wait
|
||||
set the bit again and waking the number of waiters that could grab a
|
||||
token. There are two additional properties we need to ensure:
|
||||
(1) We make sure that whenever unsetting the bit, we see the increment of
|
||||
nwaiters by the other thread that set the bit. IOW, we will notice if
|
||||
we make a mistake.
|
||||
(2) When setting the nwaiters bit, we make sure that we see the unsetting
|
||||
of the bit by another waiter that happened before us. This avoids having
|
||||
to blindly set the bit whenever we need to block on it. We set/unset
|
||||
the bit while having incremented nwaiters (i.e., are a registered
|
||||
waiter), and the problematic case only happens when one waiter indeed
|
||||
followed another (i.e., nwaiters was never larger than 1); thus, this
|
||||
works similarly as with a critical section using nwaiters (see the MOs
|
||||
and related comments below).
|
||||
|
||||
An alternative approach would be to unset the bit after decrementing
|
||||
nwaiters; however, that would result in needing Dekker-like
|
||||
synchronization and thus full memory barriers. We also would not be able
|
||||
to prevent misspeculation, so this alternative scheme does not seem
|
||||
beneficial. */
|
||||
unsigned int v;
|
||||
|
||||
/* Add a waiter. We need acquire MO so this synchronizes with the release
|
||||
MO we use when decrementing nwaiters below; it ensures that if another
|
||||
waiter unset the bit before us, we see that and set it again. Also see
|
||||
property (2) above. */
|
||||
atomic_fetch_add_acquire (&sem->nwaiters, 1);
|
||||
|
||||
pthread_cleanup_push (__sem_wait_cleanup, sem);
|
||||
|
||||
/* Wait for a token to be available. Retry until we can grab one. */
|
||||
/* We do not need any ordering wrt. to this load's reads-from, so relaxed
|
||||
MO is sufficient. The acquire MO above ensures that in the problematic
|
||||
case, we do see the unsetting of the bit by another waiter. */
|
||||
v = atomic_load_relaxed (&sem->value);
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
/* We are about to block, so make sure that the nwaiters bit is
|
||||
set. We need release MO on the CAS to ensure that when another
|
||||
waiter unsets the nwaiters bit, it will also observe that we
|
||||
incremented nwaiters in the meantime (also see the unsetting of
|
||||
the bit below). Relaxed MO on CAS failure is sufficient (see
|
||||
above). */
|
||||
do
|
||||
{
|
||||
if ((v & SEM_NWAITERS_MASK) != 0)
|
||||
break;
|
||||
}
|
||||
while (!atomic_compare_exchange_weak_release (&sem->value,
|
||||
&v, v | SEM_NWAITERS_MASK));
|
||||
/* If there is no token, wait. */
|
||||
if ((v >> SEM_VALUE_SHIFT) == 0)
|
||||
{
|
||||
/* See __HAVE_64B_ATOMICS variant. */
|
||||
err = do_futex_wait64 (sem, abstime);
|
||||
if (err == ETIMEDOUT || err == EINTR)
|
||||
{
|
||||
__set_errno (err);
|
||||
err = -1;
|
||||
goto error;
|
||||
}
|
||||
err = 0;
|
||||
/* We blocked, so there might be a token now. Relaxed MO is
|
||||
sufficient (see above). */
|
||||
v = atomic_load_relaxed (&sem->value);
|
||||
}
|
||||
}
|
||||
/* If there is no token, we must not try to grab one. */
|
||||
while ((v >> SEM_VALUE_SHIFT) == 0);
|
||||
}
|
||||
/* Try to grab a token. We need acquire MO so this synchronizes with
|
||||
all token providers (i.e., the RMW operation we read from or all those
|
||||
before it in modification order; also see sem_post). */
|
||||
while (!atomic_compare_exchange_weak_acquire (&sem->value,
|
||||
&v, v - (1 << SEM_VALUE_SHIFT)));
|
||||
|
||||
error:
|
||||
pthread_cleanup_pop (0);
|
||||
|
||||
__sem_wait_32_finish (sem);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Stop being a registered waiter (non-64b-atomics code only). */
|
||||
#if !__HAVE_64B_ATOMICS
|
||||
static void
|
||||
|
||||
+2
-1
@@ -62,7 +62,8 @@ routines := \
|
||||
spawnattr_getsigmask spawnattr_getschedpolicy spawnattr_getschedparam \
|
||||
spawnattr_setsigmask spawnattr_setschedpolicy spawnattr_setschedparam \
|
||||
posix_madvise \
|
||||
get_child_max sched_cpucount sched_cpualloc sched_cpufree
|
||||
get_child_max sched_cpucount sched_cpualloc sched_cpufree \
|
||||
sched_rr_gi64 nanosleep64
|
||||
|
||||
aux := init-posix environ
|
||||
tests := test-errno tstgetopt testfnm runtests runptests \
|
||||
|
||||
@@ -137,6 +137,10 @@ libc {
|
||||
GLIBC_2.27 {
|
||||
glob; glob64;
|
||||
}
|
||||
GLIBC_2.29 {
|
||||
__sched_rr_get_interval_time64;
|
||||
__nanosleep64;
|
||||
}
|
||||
GLIBC_PRIVATE {
|
||||
__libc_fork; __libc_pread; __libc_pwrite;
|
||||
__nanosleep_nocancel; __pause_nocancel;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <features.h>
|
||||
#include <bits/wordsize.h>
|
||||
#include <bits/timesize.h>
|
||||
|
||||
/* Convenience types. */
|
||||
typedef unsigned char __u_char;
|
||||
@@ -138,6 +139,7 @@ __extension__ typedef unsigned long long int __uintmax_t;
|
||||
# error
|
||||
#endif
|
||||
#include <bits/typesizes.h> /* Defines __*_T_TYPE macros. */
|
||||
#include <bits/time64.h> /* Defines __TIME*_T_TYPE macros. */
|
||||
|
||||
|
||||
__STD_TYPE __DEV_T_TYPE __dev_t; /* Type of device numbers. */
|
||||
@@ -211,6 +213,12 @@ __STD_TYPE __U32_TYPE __socklen_t;
|
||||
It is not currently necessary for this to be machine-specific. */
|
||||
typedef int __sig_atomic_t;
|
||||
|
||||
#if __TIMESIZE == 64
|
||||
# define __time64_t __time_t
|
||||
#else
|
||||
__STD_TYPE __TIME64_T_TYPE __time64_t; /* Seconds since the Epoch. */
|
||||
#endif
|
||||
|
||||
#undef __STD_TYPE
|
||||
|
||||
#endif /* bits/types.h */
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Pause execution for a number of nanoseconds
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Pause execution for a number of nanoseconds. */
|
||||
int
|
||||
__nanosleep64 (const struct __timespec64 *requested_time,
|
||||
struct __timespec64 *remaining)
|
||||
{
|
||||
struct timespec treq32, *treqp32 = NULL;
|
||||
struct timespec trem32, *tremp32 = NULL;
|
||||
|
||||
if (requested_time)
|
||||
{
|
||||
if (requested_time->tv_sec > INT_MAX)
|
||||
{
|
||||
__set_errno(EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
treq32.tv_sec = requested_time->tv_sec;
|
||||
treq32.tv_nsec = requested_time->tv_nsec;
|
||||
treqp32 = & treq32;
|
||||
}
|
||||
|
||||
if (remaining)
|
||||
tremp32 = &trem32;
|
||||
|
||||
int result = __nanosleep(treqp32, tremp32);
|
||||
|
||||
if (result == 1 && errno == EINTR && remaining)
|
||||
{
|
||||
remaining->tv_sec = trem32.tv_sec;
|
||||
remaining->tv_nsec = trem32.tv_nsec;
|
||||
remaining->tv_pad = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -74,6 +74,15 @@ extern int sched_get_priority_max (int __algorithm) __THROW;
|
||||
extern int sched_get_priority_min (int __algorithm) __THROW;
|
||||
|
||||
/* Get the SCHED_RR interval for the named process. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (sched_rr_get_interval,
|
||||
(__pid_t __pid, struct timespec *__t),
|
||||
__sched_rr_get_interval_time64) __THROW;
|
||||
# else
|
||||
# define sched_rr_get_interval __sched_rr_get_interval_time64
|
||||
# endif
|
||||
#endif
|
||||
extern int sched_rr_get_interval (__pid_t __pid, struct timespec *__t) __THROW;
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Get the SCHED_RR interval for the named process.
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <y2038-support.h>
|
||||
|
||||
int
|
||||
__sched_rr_get_interval_time64 (pid_t pid, struct __timespec64 *t)
|
||||
{
|
||||
struct timespec ts32;
|
||||
int result;
|
||||
|
||||
if (t == NULL)
|
||||
{
|
||||
__set_errno(EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __NR_sched_rr_get_interval_time64
|
||||
if (__y2038_linux_support > 0)
|
||||
{
|
||||
result = INLINE_SYSCALL(sched_rr_get_interval_time64, 2, pid, t);
|
||||
if (result == 0 || errno != ENOSYS)
|
||||
return result;
|
||||
__y2038_linux_support = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
result = __sched_rr_get_interval(pid, &ts32);
|
||||
if (result == 0)
|
||||
{
|
||||
t->tv_sec = ts32.tv_sec;
|
||||
t->tv_nsec = ts32.tv_nsec;
|
||||
t->tv_pad = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@ headers := sys/resource.h bits/resource.h sys/vlimit.h sys/vtimes.h \
|
||||
ulimit.h bits/types/struct_rusage.h
|
||||
|
||||
routines := getrlimit setrlimit getrlimit64 setrlimit64 getrusage ulimit \
|
||||
vlimit vtimes getpriority setpriority nice
|
||||
vlimit vtimes getpriority setpriority nice getrusage64
|
||||
|
||||
tests = tst-getrlimit bug-ulimit1
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ libc {
|
||||
# s*
|
||||
setrlimit64;
|
||||
}
|
||||
GLIBC_2.29 {
|
||||
__getrusage64;
|
||||
}
|
||||
GLIBC_PRIVATE {
|
||||
__getrlimit;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <bits/types.h>
|
||||
#include <bits/types/struct_timeval.h>
|
||||
#include <bits/types/struct_timeval64.h>
|
||||
|
||||
/* Structure which says how much of each resource has been used. If
|
||||
the system does not keep track of a particular value, the struct
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/* Return resource usage for the current process.
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <include/time.h>
|
||||
#include <sysdep.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Structure which says how much of each resource has been used. */
|
||||
/* 64-bit time version */
|
||||
/* The purpose of all the unions is to have the kernel-compatible layout
|
||||
while keeping the API type as 'long int', and among machines where
|
||||
__syscall_slong_t is not 'long int', this only does the right thing
|
||||
for little-endian ones, like x32. */
|
||||
struct __rusage64
|
||||
{
|
||||
/* Total amount of user time used. */
|
||||
struct __timeval64 ru_utime;
|
||||
/* Total amount of system time used. */
|
||||
struct __timeval64 ru_stime;
|
||||
/* Maximum resident set size (in kilobytes). */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_maxrss;
|
||||
__syscall_slong_t __ru_maxrss_word;
|
||||
};
|
||||
/* Amount of sharing of text segment memory
|
||||
with other processes (kilobyte-seconds). */
|
||||
/* Maximum resident set size (in kilobytes). */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_ixrss;
|
||||
__syscall_slong_t __ru_ixrss_word;
|
||||
};
|
||||
/* Amount of data segment memory used (kilobyte-seconds). */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_idrss;
|
||||
__syscall_slong_t __ru_idrss_word;
|
||||
};
|
||||
/* Amount of stack memory used (kilobyte-seconds). */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_isrss;
|
||||
__syscall_slong_t __ru_isrss_word;
|
||||
};
|
||||
/* Number of soft page faults (i.e. those serviced by reclaiming
|
||||
a page from the list of pages awaiting reallocation. */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_minflt;
|
||||
__syscall_slong_t __ru_minflt_word;
|
||||
};
|
||||
/* Number of hard page faults (i.e. those that required I/O). */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_majflt;
|
||||
__syscall_slong_t __ru_majflt_word;
|
||||
};
|
||||
/* Number of times a process was swapped out of physical memory. */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_nswap;
|
||||
__syscall_slong_t __ru_nswap_word;
|
||||
};
|
||||
/* Number of input operations via the file system. Note: This
|
||||
and `ru_oublock' do not include operations with the cache. */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_inblock;
|
||||
__syscall_slong_t __ru_inblock_word;
|
||||
};
|
||||
/* Number of output operations via the file system. */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_oublock;
|
||||
__syscall_slong_t __ru_oublock_word;
|
||||
};
|
||||
/* Number of IPC messages sent. */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_msgsnd;
|
||||
__syscall_slong_t __ru_msgsnd_word;
|
||||
};
|
||||
/* Number of IPC messages received. */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_msgrcv;
|
||||
__syscall_slong_t __ru_msgrcv_word;
|
||||
};
|
||||
/* Number of signals delivered. */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_nsignals;
|
||||
__syscall_slong_t __ru_nsignals_word;
|
||||
};
|
||||
/* Number of voluntary context switches, i.e. because the process
|
||||
gave up the process before it had to (usually to wait for some
|
||||
resource to be available). */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_nvcsw;
|
||||
__syscall_slong_t __ru_nvcsw_word;
|
||||
};
|
||||
/* Number of involuntary context switches, i.e. a higher priority process
|
||||
became runnable or the current process used up its time slice. */
|
||||
__extension__ union
|
||||
{
|
||||
long int ru_nivcsw;
|
||||
__syscall_slong_t __ru_nivcsw_word;
|
||||
};
|
||||
};
|
||||
|
||||
int __getrusage64 (__rusage_who_t __who, struct __rusage64 *__usage)
|
||||
{
|
||||
int result;
|
||||
struct rusage usage32;
|
||||
|
||||
result = INLINE_SYSCALL(getrusage, 2, __who, &usage32);
|
||||
/* Copy fields from 32-bit into 64-bit rusage structure */
|
||||
/* Total amount of user time used. */
|
||||
__usage->ru_utime.tv_sec = usage32.ru_utime.tv_sec;
|
||||
__usage->ru_utime.tv_usec = usage32.ru_utime.tv_usec;
|
||||
/* Total amount of system time used. */
|
||||
__usage->ru_stime.tv_sec = usage32.ru_stime.tv_sec;
|
||||
__usage->ru_stime.tv_usec = usage32.ru_stime.tv_usec;
|
||||
/* Maximum resident set size (in kilobytes). */
|
||||
__usage->ru_maxrss = usage32.ru_maxrss;
|
||||
/* Amount of sharing of text segment memory
|
||||
with other processes (kilobyte-seconds). */
|
||||
/* Maximum resident set size (in kilobytes). */
|
||||
__usage->ru_ixrss = usage32.ru_ixrss;
|
||||
/* Amount of data segment memory used (kilobyte-seconds). */
|
||||
__usage->ru_idrss = usage32.ru_idrss;
|
||||
/* Amount of stack memory used (kilobyte-seconds). */
|
||||
__usage->ru_isrss = usage32.ru_isrss;
|
||||
/* Number of soft page faults (i.e. those serviced by reclaiming
|
||||
a page from the list of pages awaiting reallocation. */
|
||||
__usage->ru_minflt = usage32.ru_minflt;
|
||||
/* Number of hard page faults (i.e. those that required I/O). */
|
||||
__usage->ru_majflt = usage32.ru_majflt;
|
||||
/* Number of times a process was swapped out of physical memory. */
|
||||
__usage->ru_nswap = usage32.ru_nswap;
|
||||
/* Number of input operations via the file system. Note: This
|
||||
and `ru_oublock' do not include operations with the cache. */
|
||||
__usage->ru_inblock = usage32.ru_inblock;
|
||||
/* Number of output operations via the file system. */
|
||||
__usage->ru_oublock = usage32.ru_oublock;
|
||||
/* Number of IPC messages sent. */
|
||||
__usage->ru_msgsnd = usage32.ru_msgsnd;
|
||||
/* Number of IPC messages received. */
|
||||
__usage->ru_msgrcv = usage32.ru_msgrcv;
|
||||
/* Number of signals delivered. */
|
||||
__usage->ru_nsignals = usage32.ru_nsignals;
|
||||
/* Number of voluntary context switches, i.e. because the process
|
||||
gave up the process before it had to (usually to wait for some
|
||||
resource to be available). */
|
||||
__usage->ru_nvcsw = usage32.ru_nvcsw;
|
||||
/* Number of involuntary context switches, i.e. a higher priority process
|
||||
became runnable or the current process used up its time slice. */
|
||||
__usage->ru_nivcsw = usage32.ru_nivcsw;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -84,6 +84,15 @@ extern int setrlimit64 (__rlimit_resource_t __resource,
|
||||
|
||||
/* Return resource usage information on process indicated by WHO
|
||||
and put it in *USAGE. Returns 0 for success, -1 for failure. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (getrusage, (__rusage_who_t __who,
|
||||
struct rusage *__usage),
|
||||
__getrusage64) __THROW;
|
||||
# else
|
||||
# define getrusage __getrusage64
|
||||
# endif
|
||||
#endif
|
||||
extern int getrusage (__rusage_who_t __who, struct rusage *__usage) __THROW;
|
||||
|
||||
/* Return the highest priority of any process specified by WHICH and WHO
|
||||
|
||||
+4
-2
@@ -32,11 +32,13 @@ clock-routines := get_clockfreq clock_getcpuclockid \
|
||||
clock_getres clock_gettime clock_settime \
|
||||
clock_nanosleep
|
||||
timer-routines := timer_create timer_delete timer_getoverr \
|
||||
timer_gettime timer_settime
|
||||
timer_gettime timer_settime \
|
||||
timerfd_gettime64 timerfd_settime64
|
||||
shm-routines := shm_open shm_unlink
|
||||
mq-routines := mq_open mq_close mq_unlink mq_getattr mq_setattr \
|
||||
mq_notify mq_send mq_receive mq_timedsend \
|
||||
mq_timedreceive
|
||||
mq_timedreceive \
|
||||
mq_timedreceiv_time64 mq_timedsend_time64
|
||||
|
||||
routines = $(clock-routines)
|
||||
|
||||
|
||||
@@ -37,4 +37,13 @@ librt {
|
||||
GLIBC_2.7 {
|
||||
__mq_open_2;
|
||||
}
|
||||
GLIBC_2.29 {
|
||||
__timer_gettime64;
|
||||
__timer_settime64;
|
||||
__timerfd_gettime64;
|
||||
__timerfd_settime64;
|
||||
__mq_timedreceive64;
|
||||
__mq_timedsend_time64;
|
||||
__aio_suspend64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/* Receive the oldest from highest priority messages in message queue
|
||||
MQDES, stop waiting if ABS_TIMEOUT expires.
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <mqueue.h>
|
||||
#include <y2038-support.h>
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
ssize_t
|
||||
__mq_timedreceiv_time64 (mqd_t mqdes, char *__restrict msg_ptr, size_t msg_len,
|
||||
unsigned int *__restrict msg_prio,
|
||||
const struct __timespec64 *__restrict abs_timeout)
|
||||
{
|
||||
struct timespec ts32, *tsp32 = NULL;
|
||||
|
||||
#ifdef __NR_timedreceiv_time64
|
||||
int result;
|
||||
|
||||
if (__y2038_get_kernel_support () > 0)
|
||||
{
|
||||
result = INLINE_SYSCALL (mq_timedreceiv_time64, 5, mqdes, msg_ptr, msg_len,
|
||||
msg_prio, abs_timeout);
|
||||
if (result == 0 || errno != ENOSYS)
|
||||
return result;
|
||||
__y2038_set_kernel_support (-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (abs_timeout)
|
||||
{
|
||||
ts32.tv_sec = abs_timeout->tv_sec;
|
||||
ts32.tv_nsec = abs_timeout->tv_nsec;
|
||||
tsp32 = &ts32;
|
||||
}
|
||||
return mq_timedreceive(mqdes, msg_ptr, msg_len, msg_prio, tsp32);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Add message pointed by MSG_PTR to message queue MQDES, stop blocking
|
||||
on full message queue if ABS_TIMEOUT expires.
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <mqueue.h>
|
||||
#include <y2038-support.h>
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__mq_timedsend_time64 (mqd_t mqdes, const char *msg_ptr, size_t msg_len,
|
||||
unsigned int msg_prio, const struct __timespec64 *abs_timeout)
|
||||
{
|
||||
struct timespec ts32, *tsp32 = NULL;
|
||||
#ifdef __NR_mq_timedsend_time64
|
||||
int result;
|
||||
|
||||
if (__y2038_get_kernel_support () > 0)
|
||||
{
|
||||
result = INLINE_SYSCALL (mq_timedsend_time64, 5, mqdes, msg_ptr,
|
||||
msg_len, msg_prio, abs_timeout);
|
||||
if (result == 0 || errno != ENOSYS)
|
||||
return result;
|
||||
__y2038_set_kernel_support (-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (abs_timeout)
|
||||
{
|
||||
ts32.tv_sec = abs_timeout->tv_sec;
|
||||
ts32.tv_nsec = abs_timeout->tv_nsec;
|
||||
tsp32 = &ts32;
|
||||
}
|
||||
return mq_timedsend(mqdes, msg_ptr, msg_len, msg_prio, tsp32);
|
||||
}
|
||||
+22
@@ -73,6 +73,17 @@ extern int mq_send (mqd_t __mqdes, const char *__msg_ptr, size_t __msg_len,
|
||||
#ifdef __USE_XOPEN2K
|
||||
/* Receive the oldest from highest priority messages in message queue
|
||||
MQDES, stop waiting if ABS_TIMEOUT expires. */
|
||||
# ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern ssize_t __REDIRECT (mq_timedreceive, (mqd_t __mqdes,
|
||||
char *__restrict __msg_ptr, size_t __msg_len,
|
||||
unsigned int *__restrict __msg_prio,
|
||||
const struct timespec *__restrict __abs_timeout),
|
||||
__mq_timedreceive64) __nonnull((2, 5));
|
||||
# else
|
||||
# define mq_timedreceive __mq_timedreceive64
|
||||
# endif
|
||||
# endif
|
||||
extern ssize_t mq_timedreceive (mqd_t __mqdes, char *__restrict __msg_ptr,
|
||||
size_t __msg_len,
|
||||
unsigned int *__restrict __msg_prio,
|
||||
@@ -81,6 +92,17 @@ extern ssize_t mq_timedreceive (mqd_t __mqdes, char *__restrict __msg_ptr,
|
||||
|
||||
/* Add message pointed by MSG_PTR to message queue MQDES, stop blocking
|
||||
on full message queue if ABS_TIMEOUT expires. */
|
||||
# ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (mq_timedsend, (mqd_t __mqdes,
|
||||
const char *__msg_ptr, size_t __msg_len,
|
||||
unsigned int __msg_prio,
|
||||
const struct timespec *__abs_timeout),
|
||||
__mq_timedsend64) __nonnull((2, 5));
|
||||
# else
|
||||
# define mq_timedsend __mq_timedsend64
|
||||
# endif
|
||||
# endif
|
||||
extern int mq_timedsend (mqd_t __mqdes, const char *__msg_ptr,
|
||||
size_t __msg_len, unsigned int __msg_prio,
|
||||
const struct timespec *__abs_timeout)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Get current value of timer TIMERID and store it in VALUE.
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
int
|
||||
__timerfd_gettime64 (int fd, struct itimerspec *value)
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
stub_warning (__timerfd_gettime64)
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Set timer TIMERID to VALUE, returning old value in OVALUE.
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
int
|
||||
__timerfd_settime64 (int fd, int flags, const struct itimerspec *value,
|
||||
struct itimerspec *ovalue)
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
stub_warning (__timerfd_settime64)
|
||||
@@ -266,6 +266,16 @@ extern int sigwaitinfo (const sigset_t *__restrict __set,
|
||||
|
||||
This function is a cancellation point and therefore not marked with
|
||||
__THROW. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (sigtimedwait, (const sigset_t *__restrict __set,
|
||||
siginfo_t *__restrict __info,
|
||||
const struct timespec *__restrict __timeout),
|
||||
__sigtimedwait64) __nonnull ((1));
|
||||
# else
|
||||
# define sigtimedwait __sigtimedwait64
|
||||
# endif
|
||||
#endif
|
||||
extern int sigtimedwait (const sigset_t *__restrict __set,
|
||||
siginfo_t *__restrict __info,
|
||||
const struct timespec *__restrict __timeout)
|
||||
|
||||
@@ -30,3 +30,13 @@ libc_hidden_def (__sigtimedwait)
|
||||
weak_alias (__sigtimedwait, sigtimedwait)
|
||||
|
||||
stub_warning (sigtimedwait)
|
||||
|
||||
int
|
||||
__sigtimedwait_time64 (const sigset_t *set, siginfo_t *info,
|
||||
const struct __timespec64 *timeout)
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stub_warning (__sigtimedwait_time64)
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ headers := stdlib.h bits/stdlib.h bits/stdlib-ldbl.h bits/stdlib-float.h \
|
||||
ucontext.h sys/ucontext.h bits/indirect-return.h \
|
||||
alloca.h fmtmsg.h \
|
||||
bits/stdlib-bsearch.h sys/random.h bits/stdint-intn.h \
|
||||
bits/stdint-uintn.h
|
||||
bits/stdint-uintn.h bits/time64.h \
|
||||
|
||||
routines := \
|
||||
atof atoi atol atoll \
|
||||
|
||||
@@ -644,3 +644,40 @@ clntudp_destroy (CLIENT *cl)
|
||||
mem_free ((caddr_t) cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
|
||||
mem_free ((caddr_t) cl, sizeof (CLIENT));
|
||||
}
|
||||
|
||||
/* 64-bit time versions */
|
||||
|
||||
CLIENT *
|
||||
__clntudp_create64 (struct sockaddr_in *raddr, u_long program, u_long version,
|
||||
struct __timeval64 wait, int *sockp)
|
||||
{
|
||||
struct timeval wait32;
|
||||
|
||||
if (wait.tv_sec > INT32_MAX || wait.tv_sec < INT32_MIN)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wait32.tv_sec = wait.tv_sec;
|
||||
wait32.tv_usec = wait.tv_usec;
|
||||
|
||||
return clntudp_create (raddr, program, version, wait32, sockp);
|
||||
}
|
||||
|
||||
CLIENT *
|
||||
__clntudp_bufcreate64 (struct sockaddr_in *raddr, u_long program, u_long version,
|
||||
struct __timeval64 wait, int *sockp, u_int sendsz,
|
||||
u_int recvsz)
|
||||
{
|
||||
struct timeval wait32;
|
||||
|
||||
if (wait.tv_sec > INT_MAX)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wait32.tv_sec = wait.tv_sec;
|
||||
wait32.tv_usec = wait.tv_usec;
|
||||
|
||||
return clntudp_bufcreate (raddr, program, version, wait32, sockp, sendsz, recvsz);
|
||||
}
|
||||
|
||||
@@ -390,3 +390,26 @@ done_broad:
|
||||
return stat;
|
||||
}
|
||||
libc_hidden_nolink_sunrpc (clnt_broadcast, GLIBC_2_0)
|
||||
|
||||
/* 64-bit-time version */
|
||||
|
||||
/* The 64-bit-time version of pmap_rmtcall.
|
||||
* Only handles 64-bit-time timeouts smaller than 2^^31 seconds.
|
||||
*/
|
||||
enum clnt_stat
|
||||
__pmap_rmtcall64 (struct sockaddr_in *addr, u_long prog, u_long vers,
|
||||
u_long proc, xdrproc_t xdrargs, caddr_t argsp,
|
||||
xdrproc_t xdrres, caddr_t resp,
|
||||
struct __timeval64 tout, u_long *port_ptr)
|
||||
{
|
||||
struct timeval tout32;
|
||||
if (tout.tv_sec > INT_MAX)
|
||||
{
|
||||
return RPC_SYSTEMERROR;
|
||||
}
|
||||
tout32.tv_sec = tout.tv_sec;
|
||||
tout32.tv_usec = tout.tv_usec;
|
||||
|
||||
return pmap_rmtcall (addr, prog, vers, proc, xdrargs, argsp, xdrres,
|
||||
resp, tout32, port_ptr);
|
||||
}
|
||||
|
||||
@@ -329,9 +329,33 @@ extern CLIENT *clnttcp_create (struct sockaddr_in *__raddr, u_long __prog,
|
||||
* u_int sendsz;
|
||||
* u_int recvsz;
|
||||
*/
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern CLIENT * __REDIRECT (clntudp_create,(struct sockaddr_in *__raddr,
|
||||
u_long __program,
|
||||
u_long __version,
|
||||
struct timeval __wait_resend,
|
||||
int *__sockp),
|
||||
__clntudp_create64) __THROW;
|
||||
# else
|
||||
# define clntudp_create __clntudp_create64
|
||||
# endif
|
||||
#endif
|
||||
extern CLIENT *clntudp_create (struct sockaddr_in *__raddr, u_long __program,
|
||||
u_long __version, struct timeval __wait_resend,
|
||||
int *__sockp) __THROW;
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern CLIENT * __REDIRECT (clntudp_bufcreate,(struct sockaddr_in *__raddr,
|
||||
u_long __program, u_long __version,
|
||||
struct __timeval64 __wait_resend,
|
||||
int *__sockp, u_int __sendsz,
|
||||
u_int __recvsz),
|
||||
__clntudp_bufcreate64) __THROW;
|
||||
# else
|
||||
# define clntudp_bufcreate __clntudp_bufcreate64
|
||||
# endif
|
||||
#endif
|
||||
extern CLIENT *clntudp_bufcreate (struct sockaddr_in *__raddr,
|
||||
u_long __program, u_long __version,
|
||||
struct timeval __wait_resend, int *__sockp,
|
||||
|
||||
@@ -71,6 +71,21 @@ extern bool_t pmap_set (const u_long __program, const u_long __vers,
|
||||
extern bool_t pmap_unset (const u_long __program, const u_long __vers)
|
||||
__THROW;
|
||||
extern struct pmaplist *pmap_getmaps (struct sockaddr_in *__address) __THROW;
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern enum clnt_stat __REDIRECT (pmap_rmtcall, (struct sockaddr_in *__addr,
|
||||
const u_long __prog,
|
||||
const u_long __vers,
|
||||
const u_long __proc,
|
||||
xdrproc_t __xdrargs,
|
||||
caddr_t __argsp, xdrproc_t __xdrres,
|
||||
caddr_t __resp, struct timeval __tout,
|
||||
u_long *__port_ptr),
|
||||
__pmap_rmtcall64) __THROW;
|
||||
# else
|
||||
# define pmap_rmtcall __pmap_rmtcall64
|
||||
# endif
|
||||
#endif
|
||||
extern enum clnt_stat pmap_rmtcall (struct sockaddr_in *__addr,
|
||||
const u_long __prog,
|
||||
const u_long __vers,
|
||||
|
||||
@@ -71,4 +71,43 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define AIO_MISC_WAIT_T64(result, futex, timeout, cancel) \
|
||||
do { \
|
||||
volatile unsigned int *futexaddr = &futex; \
|
||||
unsigned int oldval = futex; \
|
||||
\
|
||||
if (oldval != 0) \
|
||||
{ \
|
||||
pthread_mutex_unlock (&__aio_requests_mutex); \
|
||||
\
|
||||
int oldtype; \
|
||||
if (cancel) \
|
||||
oldtype = LIBC_CANCEL_ASYNC (); \
|
||||
\
|
||||
int status; \
|
||||
do \
|
||||
{ \
|
||||
status = futex_reltimed_wait64 ((unsigned int *) futexaddr, \
|
||||
oldval, timeout, FUTEX_PRIVATE);\
|
||||
if (status != EAGAIN) \
|
||||
break; \
|
||||
\
|
||||
oldval = *futexaddr; \
|
||||
} \
|
||||
while (oldval != 0); \
|
||||
\
|
||||
if (cancel) \
|
||||
LIBC_CANCEL_RESET (oldtype); \
|
||||
\
|
||||
if (status == EINTR) \
|
||||
result = EINTR; \
|
||||
else if (status == ETIMEDOUT) \
|
||||
result = EAGAIN; \
|
||||
else \
|
||||
assert (status == 0 || status == EAGAIN); \
|
||||
\
|
||||
pthread_mutex_lock (&__aio_requests_mutex); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#include_next <aio_misc.h>
|
||||
|
||||
@@ -122,6 +122,10 @@ extern void __lll_lock_wait (int *futex, int private) attribute_hidden;
|
||||
extern int __lll_timedlock_wait (int *futex, const struct timespec *,
|
||||
int private) attribute_hidden;
|
||||
|
||||
extern int __lll_timedlock_wait64 (int *futex,
|
||||
const struct __timespec64 *,
|
||||
int private) attribute_hidden;
|
||||
|
||||
|
||||
/* As __lll_lock, but with a timeout. If the timeout occurs then return
|
||||
ETIMEDOUT. If ABSTIME is invalid, return EINVAL. */
|
||||
@@ -138,6 +142,19 @@ extern int __lll_timedlock_wait (int *futex, const struct timespec *,
|
||||
#define lll_timedlock(futex, abstime, private) \
|
||||
__lll_timedlock (&(futex), abstime, private)
|
||||
|
||||
#define __lll_timedlock64(futex, abstime, private) \
|
||||
({ \
|
||||
int *__futex = (futex); \
|
||||
int __val = 0; \
|
||||
\
|
||||
if (__glibc_unlikely \
|
||||
(atomic_compare_and_exchange_bool_acq (__futex, 1, 0))) \
|
||||
__val = __lll_timedlock_wait64 (__futex, abstime, private); \
|
||||
__val; \
|
||||
})
|
||||
#define lll_timedlock64(futex, abstime, private) \
|
||||
__lll_timedlock64 (&(futex), abstime, private)
|
||||
|
||||
|
||||
/* This is an expression rather than a statement even though its value is
|
||||
void, so that it can be used in a comma expression or as an expression
|
||||
|
||||
@@ -765,6 +765,17 @@ extern int pthread_mutex_lock (pthread_mutex_t *__mutex)
|
||||
|
||||
#ifdef __USE_XOPEN2K
|
||||
/* Wait until lock becomes available, or specified time passes. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (pthread_mutex_timedlock,
|
||||
(pthread_mutex_t *__restrict __mutex,
|
||||
const struct timespec *__restrict __abstime),
|
||||
__pthread_mutex_timedlock64)
|
||||
__THROWNL __nonnull ((1, 2));
|
||||
# else
|
||||
# define pthread_mutex_timedlock __pthread_mutex_timedlock64
|
||||
# endif
|
||||
#endif
|
||||
extern int pthread_mutex_timedlock (pthread_mutex_t *__restrict __mutex,
|
||||
const struct timespec *__restrict
|
||||
__abstime) __THROWNL __nonnull ((1, 2));
|
||||
@@ -904,6 +915,17 @@ extern int pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock)
|
||||
|
||||
# ifdef __USE_XOPEN2K
|
||||
/* Try to acquire read lock for RWLOCK or return after specfied time. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (pthread_rwlock_timedrdlock,
|
||||
(pthread_rwlock_t *__restrict __rwlock,
|
||||
const struct timespec *__restrict __abstime),
|
||||
__pthread_rwlock_timedrdlock64)
|
||||
__THROWNL __nonnull ((1, 2));
|
||||
# else
|
||||
# define pthread_rwlock_timedrdlock __pthread_rwlock_timedrdlock64
|
||||
# endif
|
||||
#endif
|
||||
extern int pthread_rwlock_timedrdlock (pthread_rwlock_t *__restrict __rwlock,
|
||||
const struct timespec *__restrict
|
||||
__abstime) __THROWNL __nonnull ((1, 2));
|
||||
@@ -919,6 +941,17 @@ extern int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock)
|
||||
|
||||
# ifdef __USE_XOPEN2K
|
||||
/* Try to acquire write lock for RWLOCK or return after specfied time. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (pthread_rwlock_timedwrlock,
|
||||
(pthread_rwlock_t *__restrict __rwlock,
|
||||
const struct timespec *__restrict __abstime),
|
||||
__pthread_rwlock_timedwrlock64)
|
||||
__THROWNL __nonnull ((1, 2));
|
||||
# else
|
||||
# define pthread_rwlock_timedwrlock __pthread_rwlock_timedwrlock64
|
||||
# endif
|
||||
#endif
|
||||
extern int pthread_rwlock_timedwrlock (pthread_rwlock_t *__restrict __rwlock,
|
||||
const struct timespec *__restrict
|
||||
__abstime) __THROWNL __nonnull ((1, 2));
|
||||
@@ -998,6 +1031,18 @@ extern int pthread_cond_wait (pthread_cond_t *__restrict __cond,
|
||||
|
||||
This function is a cancellation point and therefore not marked with
|
||||
__THROW. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern int __REDIRECT (pthread_cond_timedwait,
|
||||
(pthread_cond_t *__restrict __cond,
|
||||
pthread_mutex_t *__restrict __mutex,
|
||||
const struct timespec *__restrict __abstime),
|
||||
__pthread_cond_timedwait64)
|
||||
__nonnull ((1, 2, 3));
|
||||
# else
|
||||
# define pthread_cond_timedwait __pthread_cond_timedwait64
|
||||
# endif
|
||||
#endif
|
||||
extern int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
|
||||
pthread_mutex_t *__restrict __mutex,
|
||||
const struct timespec *__restrict __abstime)
|
||||
|
||||
@@ -23,12 +23,11 @@
|
||||
#include <sys/param.h>
|
||||
#include <libc-internal.h>
|
||||
|
||||
|
||||
#if HP_TIMING_AVAIL
|
||||
static long int nsec; /* Clock frequency of the processor. */
|
||||
|
||||
static int
|
||||
hp_timing_getres (struct timespec *res)
|
||||
hp_timing_getres (struct __timespec64 *res)
|
||||
{
|
||||
if (__glibc_unlikely (nsec == 0))
|
||||
{
|
||||
@@ -56,7 +55,7 @@ hp_timing_getres (struct timespec *res)
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
realtime_getres (struct timespec *res)
|
||||
realtime_getres (struct __timespec64 *res)
|
||||
{
|
||||
long int clk_tck = __sysconf (_SC_CLK_TCK);
|
||||
|
||||
@@ -73,17 +72,16 @@ realtime_getres (struct timespec *res)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Get resolution of clock. */
|
||||
int
|
||||
__clock_getres (clockid_t clock_id, struct timespec *res)
|
||||
__clock_getres_time64 (clockid_t clock_id, struct __timespec64 *res)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
switch (clock_id)
|
||||
{
|
||||
#ifdef SYSDEP_GETRES
|
||||
SYSDEP_GETRES;
|
||||
#ifdef SYSDEP_GETRES64
|
||||
SYSDEP_GETRES64;
|
||||
#endif
|
||||
|
||||
#ifndef HANDLED_REALTIME
|
||||
@@ -93,8 +91,8 @@ __clock_getres (clockid_t clock_id, struct timespec *res)
|
||||
#endif /* handled REALTIME */
|
||||
|
||||
default:
|
||||
#ifdef SYSDEP_GETRES_CPU
|
||||
SYSDEP_GETRES_CPU;
|
||||
#ifdef SYSDEP_GETRES_CPU64
|
||||
SYSDEP_GETRES_CPU64;
|
||||
#endif
|
||||
#if HP_TIMING_AVAIL
|
||||
if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1))
|
||||
@@ -115,4 +113,19 @@ __clock_getres (clockid_t clock_id, struct timespec *res)
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
__clock_getres (clockid_t clock_id, struct timespec *res)
|
||||
{
|
||||
struct __timespec64 ts64;
|
||||
int retval = __clock_getres_time64 (clock_id, &ts64);
|
||||
if (retval == 0)
|
||||
{
|
||||
// We assume we never run with a CPU clock period greater than
|
||||
// 2**31 seconds and therefore we do not check the seconds field
|
||||
res->tv_sec = ts64.tv_sec;
|
||||
res->tv_nsec = ts64.tv_nsec;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
weak_alias (__clock_getres, clock_getres)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/* Get the 64-bit current time
|
||||
*
|
||||
Copyright (C) 1991-2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stddef.h> /* For NULL. */
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
/* Return the current time as a `time_t' and also put it in *T if T is
|
||||
not NULL. Time is represented as seconds from Jan 1 00:00:00 1970. */
|
||||
|
||||
__time64_t
|
||||
__time64 (__time64_t *t)
|
||||
{
|
||||
struct __timeval64 tv;
|
||||
__time64_t result;
|
||||
|
||||
if (__gettimeofday64 (&tv, (struct timezone *) NULL))
|
||||
result = (__time64_t) -1;
|
||||
else
|
||||
result = tv.tv_sec;
|
||||
|
||||
if (t != NULL)
|
||||
*t = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <sysdep.h>
|
||||
#include <errno.h>
|
||||
#include <utime.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
/* Set the access and modification times of FILE to those given in TIMES.
|
||||
If TIMES is NULL, set them to the current time. */
|
||||
int
|
||||
__utime64 (const char *file, const struct __utimbuf64 *times)
|
||||
{
|
||||
struct __timeval64 timevals[2];
|
||||
struct __timeval64 *tvp;
|
||||
|
||||
if (times != NULL)
|
||||
{
|
||||
timevals[0].tv_sec = (time_t) times->actime;
|
||||
timevals[0].tv_usec = 0L;
|
||||
timevals[1].tv_sec = (time_t) times->modtime;
|
||||
timevals[1].tv_usec = 0L;
|
||||
tvp = timevals;
|
||||
}
|
||||
else
|
||||
tvp = NULL;
|
||||
|
||||
return __utimes64 (file, tvp);
|
||||
}
|
||||
@@ -251,3 +251,167 @@ aio_suspend (const struct aiocb *const list[], int nent,
|
||||
}
|
||||
|
||||
weak_alias (aio_suspend, aio_suspend64)
|
||||
|
||||
#ifdef DONT_NEED_AIO_MISC_COND
|
||||
static int
|
||||
__attribute__ ((noinline))
|
||||
do_aio_misc_wait64 (unsigned int *cntr,
|
||||
const struct __timespec64 *timeout)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
AIO_MISC_WAIT_T64 (result, *cntr, timeout, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
__aio_suspend64 (const struct aiocb *const list[], int nent,
|
||||
const struct __timespec64 *timeout)
|
||||
{
|
||||
if (__glibc_unlikely (nent < 0))
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct waitlist waitlist[nent];
|
||||
struct requestlist *requestlist[nent];
|
||||
#ifndef DONT_NEED_AIO_MISC_COND
|
||||
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
#endif
|
||||
int cnt;
|
||||
bool any = false;
|
||||
int result = 0;
|
||||
unsigned int cntr = 1;
|
||||
|
||||
/* Request the mutex. */
|
||||
pthread_mutex_lock (&__aio_requests_mutex);
|
||||
|
||||
/* There is not yet a finished request. Signal the request that
|
||||
we are working for it. */
|
||||
for (cnt = 0; cnt < nent; ++cnt)
|
||||
if (list[cnt] != NULL)
|
||||
{
|
||||
if (list[cnt]->__error_code == EINPROGRESS)
|
||||
{
|
||||
requestlist[cnt] = __aio_find_req ((aiocb_union *) list[cnt]);
|
||||
|
||||
if (requestlist[cnt] != NULL)
|
||||
{
|
||||
#ifndef DONT_NEED_AIO_MISC_COND
|
||||
waitlist[cnt].cond = &cond;
|
||||
#endif
|
||||
waitlist[cnt].result = NULL;
|
||||
waitlist[cnt].next = requestlist[cnt]->waiting;
|
||||
waitlist[cnt].counterp = &cntr;
|
||||
waitlist[cnt].sigevp = NULL;
|
||||
#ifdef BROKEN_THREAD_SIGNALS
|
||||
waitlist[cnt].caller_pid = 0; /* Not needed. */
|
||||
#endif
|
||||
requestlist[cnt]->waiting = &waitlist[cnt];
|
||||
any = true;
|
||||
}
|
||||
else
|
||||
/* We will never suspend. */
|
||||
break;
|
||||
}
|
||||
else
|
||||
/* We will never suspend. */
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Only if none of the entries is NULL or finished to be wait. */
|
||||
if (cnt == nent && any)
|
||||
{
|
||||
struct clparam clparam =
|
||||
{
|
||||
.list = list,
|
||||
.waitlist = waitlist,
|
||||
.requestlist = requestlist,
|
||||
#ifndef DONT_NEED_AIO_MISC_COND
|
||||
.cond = &cond,
|
||||
#endif
|
||||
.nent = nent
|
||||
};
|
||||
|
||||
pthread_cleanup_push (cleanup, &clparam);
|
||||
|
||||
#ifdef DONT_NEED_AIO_MISC_COND
|
||||
result = do_aio_misc_wait64 (&cntr, timeout);
|
||||
#else
|
||||
if (timeout == NULL)
|
||||
result = pthread_cond_wait (&cond, &__aio_requests_mutex);
|
||||
else
|
||||
{
|
||||
/* We have to convert the relative timeout value into an
|
||||
absolute time value with pthread_cond_timedwait expects. */
|
||||
struct timeval now;
|
||||
struct timespec abstime;
|
||||
|
||||
__gettimeofday (&now, NULL);
|
||||
abstime.tv_nsec = timeout->tv_nsec + now.tv_usec * 1000;
|
||||
abstime.tv_sec = timeout->tv_sec + now.tv_sec;
|
||||
if (abstime.tv_nsec >= 1000000000)
|
||||
{
|
||||
abstime.tv_nsec -= 1000000000;
|
||||
abstime.tv_sec += 1;
|
||||
}
|
||||
|
||||
result = __pthread_cond_timedwait64 (&cond,
|
||||
&__aio_requests_mutex,
|
||||
&abstime);
|
||||
}
|
||||
#endif
|
||||
|
||||
pthread_cleanup_pop (0);
|
||||
}
|
||||
|
||||
/* Now remove the entry in the waiting list for all requests
|
||||
which didn't terminate. */
|
||||
while (cnt-- > 0)
|
||||
if (list[cnt] != NULL && list[cnt]->__error_code == EINPROGRESS)
|
||||
{
|
||||
struct waitlist **listp;
|
||||
|
||||
assert (requestlist[cnt] != NULL);
|
||||
|
||||
/* There is the chance that we cannot find our entry anymore. This
|
||||
could happen if the request terminated and restarted again. */
|
||||
listp = &requestlist[cnt]->waiting;
|
||||
while (*listp != NULL && *listp != &waitlist[cnt])
|
||||
listp = &(*listp)->next;
|
||||
|
||||
if (*listp != NULL)
|
||||
*listp = (*listp)->next;
|
||||
}
|
||||
|
||||
#ifndef DONT_NEED_AIO_MISC_COND
|
||||
/* Release the conditional variable. */
|
||||
if (__glibc_unlikely (pthread_cond_destroy (&cond) != 0))
|
||||
/* This must never happen. */
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
#ifndef DONT_NEED_AIO_MISC_COND
|
||||
/* An error occurred. Possibly it's ETIMEDOUT. We have to translate
|
||||
the timeout error report of `pthread_cond_timedwait' to the
|
||||
form expected from `aio_suspend'. */
|
||||
if (result == ETIMEDOUT)
|
||||
__set_errno (EAGAIN);
|
||||
else
|
||||
#endif
|
||||
__set_errno (result);
|
||||
|
||||
result = -1;
|
||||
}
|
||||
|
||||
/* Release the mutex. */
|
||||
pthread_mutex_unlock (&__aio_requests_mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,16 @@ extern int sem_wait (sem_t *__sem);
|
||||
|
||||
This function is a cancellation point and therefore not marked with
|
||||
__THROW. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
# if defined(__REDIRECT)
|
||||
extern struct tm * __REDIRECT (sem_timedwait,
|
||||
(sem_t *__restrict __sem,
|
||||
const struct timespec *__restrict __abstime),
|
||||
__sem_timedwait64);
|
||||
# else
|
||||
# define sem_timedwait __sem_timedwait64
|
||||
# endif
|
||||
#endif
|
||||
extern int sem_timedwait (sem_t *__restrict __sem,
|
||||
const struct timespec *__restrict __abstime);
|
||||
#endif
|
||||
|
||||
@@ -32,12 +32,12 @@ static hp_timing_t freq;
|
||||
|
||||
|
||||
/* This function is defined in the thread library. */
|
||||
extern int __pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq,
|
||||
struct timespec *tp)
|
||||
extern int __pthread_clock_gettime64 (clockid_t clock_id, hp_timing_t freq,
|
||||
struct __timespec64 *tp)
|
||||
__attribute__ ((__weak__));
|
||||
|
||||
static int
|
||||
hp_timing_gettime (clockid_t clock_id, struct timespec *tp)
|
||||
hp_timing_gettime (clockid_t clock_id, struct __timespec64 *tp)
|
||||
{
|
||||
hp_timing_t tsc;
|
||||
|
||||
@@ -55,7 +55,7 @@ hp_timing_gettime (clockid_t clock_id, struct timespec *tp)
|
||||
|
||||
if (clock_id != CLOCK_PROCESS_CPUTIME_ID
|
||||
&& __pthread_clock_gettime != NULL)
|
||||
return __pthread_clock_gettime (clock_id, freq, tp);
|
||||
return __pthread_clock_gettime64 (clock_id, freq, tp);
|
||||
|
||||
/* Get the current counter. */
|
||||
HP_TIMING_NOW (tsc);
|
||||
@@ -76,20 +76,20 @@ hp_timing_gettime (clockid_t clock_id, struct timespec *tp)
|
||||
|
||||
|
||||
static inline int
|
||||
realtime_gettime (struct timespec *tp)
|
||||
realtime_gettime (struct __timespec64 *tp)
|
||||
{
|
||||
struct timeval tv;
|
||||
int retval = __gettimeofday (&tv, NULL);
|
||||
if (retval == 0)
|
||||
/* Convert into `timespec'. */
|
||||
TIMEVAL_TO_TIMESPEC (&tv, tp);
|
||||
valid_timeval_to_timespec64 (&tv, tp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* Get current value of CLOCK and store it in TP. */
|
||||
int
|
||||
__clock_gettime (clockid_t clock_id, struct timespec *tp)
|
||||
__clock_gettime64 (clockid_t clock_id, struct __timespec64 *tp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
@@ -103,9 +103,9 @@ __clock_gettime (clockid_t clock_id, struct timespec *tp)
|
||||
case CLOCK_REALTIME:
|
||||
{
|
||||
struct timeval tv;
|
||||
retval = __gettimeofday (&tv, NULL);
|
||||
retval = __gettimeofday (&tv32, NULL);
|
||||
if (retval == 0)
|
||||
TIMEVAL_TO_TIMESPEC (&tv, tp);
|
||||
valid_timeval_to_timespec64 (&tv32, tp);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
@@ -132,5 +132,36 @@ __clock_gettime (clockid_t clock_id, struct timespec *tp)
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
__clock_gettime (clockid_t clock_id, struct timespec *tp)
|
||||
{
|
||||
struct __timespec64 ts64;
|
||||
int res;
|
||||
|
||||
if (tp == NULL)
|
||||
{
|
||||
__set_errno(EINVAL);
|
||||
res = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = __clock_gettime64 (clock_id, &ts64);
|
||||
if (res == 0)
|
||||
{
|
||||
if (fits_in_time_t (ts64.tv_sec))
|
||||
{
|
||||
tp->tv_sec = ts64.tv_sec;
|
||||
tp->tv_nsec = ts64.tv_nsec;
|
||||
}
|
||||
else
|
||||
{
|
||||
__set_errno(EOVERFLOW);
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
weak_alias (__clock_gettime, clock_gettime)
|
||||
libc_hidden_def (__clock_gettime)
|
||||
|
||||
@@ -68,8 +68,66 @@ hp_timing_settime (clockid_t clock_id, const struct timespec *tp)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Set CLOCK to value TP. */
|
||||
int
|
||||
__clock_settime64 (clockid_t clock_id, const struct __timespec64 *tp)
|
||||
{
|
||||
#ifndef HANDLED_REALTIME
|
||||
struct timeval tv32;
|
||||
#endif
|
||||
int retval = -1;
|
||||
|
||||
/* Make sure the time cvalue is OK. */
|
||||
if (! IS_VALID_NANOSECONDS(tp->tv_nsec))
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (clock_id)
|
||||
{
|
||||
#define HANDLE_REALTIME64 \
|
||||
if (timespec64_to_timeval (tp, &tv32)) \
|
||||
{ \
|
||||
retval = __settimeofday (&tv32, NULL); \
|
||||
else \
|
||||
{ \
|
||||
__set_errno (EOVERFLOW); \
|
||||
retval = -1; \
|
||||
}
|
||||
|
||||
#ifdef SYSDEP_SETTIME64
|
||||
SYSDEP_SETTIME64;
|
||||
#endif
|
||||
|
||||
#ifndef HANDLED_REALTIME
|
||||
case CLOCK_REALTIME:
|
||||
HANDLE_REALTIME64;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
#ifdef SYSDEP_SETTIME64_CPU
|
||||
SYSDEP_SETTIME64_CPU;
|
||||
#endif
|
||||
#ifndef HANDLED_CPUTIME
|
||||
# if HP_TIMING_AVAIL
|
||||
if (CPUCLOCK_WHICH (clock_id) == CLOCK_PROCESS_CPUTIME_ID
|
||||
|| CPUCLOCK_WHICH (clock_id) == CLOCK_THREAD_CPUTIME_ID)
|
||||
retval = hp_timing_settime (clock_id, tp);
|
||||
else
|
||||
# endif
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
retval = -1;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
__clock_settime (clockid_t clock_id, const struct timespec *tp)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Set the system clock on a unix system
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h> /* For NULL. */
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Set the system clock to *WHEN. */
|
||||
|
||||
int
|
||||
__stime64 (const __time64_t *when)
|
||||
{
|
||||
struct __timeval64 tv;
|
||||
|
||||
if (when == NULL)
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*when > INT_MAX)
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tv.tv_sec = *when;
|
||||
tv.tv_usec = 0;
|
||||
return __settimeofday64 (&tv, (struct timezone *) 0);
|
||||
}
|
||||
@@ -170,6 +170,8 @@ libc {
|
||||
memfd_create;
|
||||
mlock2;
|
||||
pkey_alloc; pkey_free; pkey_set; pkey_get; pkey_mprotect;
|
||||
__ntp_gettime64;
|
||||
__ntp_gettimex64;
|
||||
}
|
||||
GLIBC_PRIVATE {
|
||||
# functions used in other libraries
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <limits.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timex.h>
|
||||
#include <include/time.h>
|
||||
|
||||
#define MAX_SEC (INT_MAX / 1000000L - 2)
|
||||
#define MIN_SEC (INT_MIN / 1000000L + 2)
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/* Copyright (C) 1995-2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timex.h>
|
||||
#include <include/time.h>
|
||||
|
||||
#define MAX_SEC (INT_MAX / 1000000L - 2)
|
||||
#define MIN_SEC (INT_MIN / 1000000L + 2)
|
||||
|
||||
#ifndef MOD_OFFSET
|
||||
#define modes mode
|
||||
#endif
|
||||
|
||||
#ifndef TIMEVAL
|
||||
#define TIMEVAL timeval
|
||||
#endif
|
||||
|
||||
#ifndef TIMEX
|
||||
#define TIMEX timex
|
||||
#endif
|
||||
|
||||
#ifndef ADJTIME
|
||||
#define ADJTIME __adjtime
|
||||
#endif
|
||||
|
||||
#ifndef ADJTIMEX
|
||||
#define NO_LOCAL_ADJTIME
|
||||
#define ADJTIMEX(x) __adjtimex (x)
|
||||
#endif
|
||||
|
||||
#ifndef LINKAGE
|
||||
#define LINKAGE
|
||||
#endif
|
||||
|
||||
int __adjtime64 (const struct __timeval64 *itv,
|
||||
struct __timeval64 *otv)
|
||||
{
|
||||
struct TIMEX tntx;
|
||||
|
||||
if (itv)
|
||||
{
|
||||
struct TIMEVAL tmp;
|
||||
|
||||
/* We will do some check here. */
|
||||
tmp.tv_sec = itv->tv_sec + itv->tv_usec / 1000000L;
|
||||
tmp.tv_usec = itv->tv_usec % 1000000L;
|
||||
if (tmp.tv_sec > MAX_SEC || tmp.tv_sec < MIN_SEC)
|
||||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
|
||||
tntx.offset = tmp.tv_usec + tmp.tv_sec * 1000000L;
|
||||
tntx.modes = ADJ_OFFSET_SINGLESHOT;
|
||||
}
|
||||
else
|
||||
tntx.modes = ADJ_OFFSET_SS_READ;
|
||||
|
||||
if (__glibc_unlikely (ADJTIMEX (&tntx) < 0))
|
||||
return -1;
|
||||
|
||||
if (otv)
|
||||
{
|
||||
if (tntx.offset < 0)
|
||||
{
|
||||
otv->tv_usec = -(-tntx.offset % 1000000);
|
||||
otv->tv_sec = -(-tntx.offset / 1000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
otv->tv_usec = tntx.offset % 1000000;
|
||||
otv->tv_sec = tntx.offset / 1000000;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
__adjtimex64(struct __timex64 *tx)
|
||||
{
|
||||
struct timex tx32;
|
||||
|
||||
if (tx == NULL)
|
||||
{
|
||||
__set_errno (EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((tx->modes & ADJ_SETOFFSET) != 0 && tx->time.tv_sec > INT_MAX)
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Implement with existing 32-bit time syscall */
|
||||
|
||||
/* Just copy everything */
|
||||
tx32.modes = tx->modes;
|
||||
tx32.offset = tx->offset;
|
||||
tx32.freq = tx->freq;
|
||||
tx32.maxerror = tx->maxerror;
|
||||
tx32.esterror = tx->esterror;
|
||||
tx32.status = tx->status;
|
||||
tx32.constant = tx->constant;
|
||||
tx32.precision = tx->precision;
|
||||
tx32.tolerance = tx->tolerance;
|
||||
tx32.time.tv_sec = tx->time.tv_sec;
|
||||
tx32.time.tv_usec = tx->time.tv_usec;
|
||||
tx32.tick = tx->tick;
|
||||
tx32.ppsfreq = tx->ppsfreq;
|
||||
tx32.jitter = tx->jitter;
|
||||
tx32.shift = tx->shift;
|
||||
tx32.stabil = tx->stabil;
|
||||
tx32.jitcnt = tx->jitcnt;
|
||||
tx32.calcnt = tx->calcnt;
|
||||
tx32.errcnt = tx->errcnt;
|
||||
tx32.stbcnt = tx->stbcnt;
|
||||
|
||||
tx32.tai = tx->tai;
|
||||
/* WARNING -- anonymous fields after TAI are not copied. */
|
||||
|
||||
int result = ADJTIMEX(&tx32);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
/* Just copy back everything */
|
||||
tx->modes = tx32.modes;
|
||||
tx->offset = tx32.offset;
|
||||
tx->freq = tx32.freq;
|
||||
tx->maxerror = tx32.maxerror;
|
||||
tx->esterror = tx32.esterror;
|
||||
tx->status = tx32.status;
|
||||
tx->constant = tx32.constant;
|
||||
tx->precision = tx32.precision;
|
||||
tx->tolerance = tx32.tolerance;
|
||||
tx->time.tv_sec = tx32.time.tv_sec;
|
||||
tx->time.tv_usec = tx32.time.tv_usec;
|
||||
tx->tick = tx32.tick;
|
||||
tx->ppsfreq = tx32.ppsfreq;
|
||||
tx->jitter = tx32.jitter;
|
||||
tx->shift = tx32.shift;
|
||||
tx->stabil = tx32.stabil;
|
||||
tx->jitcnt = tx32.jitcnt;
|
||||
tx->calcnt = tx32.calcnt;
|
||||
tx->errcnt = tx32.errcnt;
|
||||
tx->stbcnt = tx32.stbcnt;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
weak_alias (__adjtimex64, __ntp_adjtime64);
|
||||
@@ -176,6 +176,7 @@ enum __rusage_who
|
||||
};
|
||||
|
||||
#include <bits/types/struct_timeval.h>
|
||||
#include <bits/types/struct_timeval64.h>
|
||||
#include <bits/types/struct_rusage.h>
|
||||
|
||||
/* Priority limits. */
|
||||
|
||||
@@ -27,6 +27,9 @@ GLIBC_2.28 tss_create F
|
||||
GLIBC_2.28 tss_delete F
|
||||
GLIBC_2.28 tss_get F
|
||||
GLIBC_2.28 tss_set F
|
||||
GLIBC_2.29 __pthread_cond_timedwait64 F
|
||||
GLIBC_2.29 __pthread_mutex_timedlock64 F
|
||||
GLIBC_2.29 __sem_timedwait64 F
|
||||
GLIBC_2.4 _IO_flockfile F
|
||||
GLIBC_2.4 _IO_ftrylockfile F
|
||||
GLIBC_2.4 _IO_funlockfile F
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
GLIBC_2.29 __aio_suspend64 F
|
||||
GLIBC_2.29 __mq_timedreceive64 F
|
||||
GLIBC_2.29 __mq_timedsend_time64 F
|
||||
GLIBC_2.29 __timer_gettime64 F
|
||||
GLIBC_2.29 __timer_settime64 F
|
||||
GLIBC_2.29 __timerfd_gettime64 F
|
||||
GLIBC_2.29 __timerfd_settime64 F
|
||||
GLIBC_2.4 aio_cancel F
|
||||
GLIBC_2.4 aio_cancel64 F
|
||||
GLIBC_2.4 aio_error F
|
||||
|
||||
@@ -46,6 +46,22 @@ typedef __syscall_ulong_t msglen_t;
|
||||
|
||||
/* Structure of record for one message inside the kernel.
|
||||
The type `struct msg' is opaque. */
|
||||
#ifdef __USE_TIME_BITS64
|
||||
struct msqid_ds
|
||||
{
|
||||
struct ipc_perm msg_perm; /* structure describing operation permission */
|
||||
__time64_t msg_stime; /* time of last msgsnd command */
|
||||
__time64_t msg_rtime; /* time of last msgrcv command */
|
||||
__time64_t msg_ctime; /* time of last change */
|
||||
unsigned long int __msg_cbytes; /* current number of bytes on queue */
|
||||
msgqnum_t msg_qnum; /* number of messages currently on queue */
|
||||
msglen_t msg_qbytes; /* max number of bytes allowed on queue */
|
||||
__pid_t msg_lspid; /* pid of last msgsnd() */
|
||||
__pid_t msg_lrpid; /* pid of last msgrcv() */
|
||||
unsigned long int __glibc_reserved4;
|
||||
unsigned long int __glibc_reserved5;
|
||||
};
|
||||
#else
|
||||
struct msqid_ds
|
||||
{
|
||||
struct ipc_perm msg_perm; /* structure describing operation permission */
|
||||
@@ -60,6 +76,7 @@ struct msqid_ds
|
||||
__syscall_ulong_t __glibc_reserved4;
|
||||
__syscall_ulong_t __glibc_reserved5;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef __USE_MISC
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ enum __rusage_who
|
||||
};
|
||||
|
||||
#include <bits/types/struct_timeval.h>
|
||||
#include <bits/types/struct_timeval64.h>
|
||||
#include <bits/types/struct_rusage.h>
|
||||
|
||||
/* Priority limits. */
|
||||
|
||||
@@ -119,11 +119,11 @@ struct stat64
|
||||
struct timespec st_mtim; /* Time of last modification. */
|
||||
struct timespec st_ctim; /* Time of last status change. */
|
||||
# else
|
||||
__time_t st_atime; /* Time of last access. */
|
||||
time_t st_atime; /* Time of last access. */
|
||||
unsigned long int st_atimensec; /* Nscecs of last access. */
|
||||
__time_t st_mtime; /* Time of last modification. */
|
||||
time_t st_mtime; /* Time of last modification. */
|
||||
unsigned long int st_mtimensec; /* Nsecs of last modification. */
|
||||
__time_t st_ctime; /* Time of last status change. */
|
||||
time_t st_ctime; /* Time of last status change. */
|
||||
unsigned long int st_ctimensec; /* Nsecs of last status change. */
|
||||
# endif
|
||||
__ino64_t st_ino; /* File serial number. */
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <bits/types.h>
|
||||
#include <bits/types/struct_timeval.h>
|
||||
#include <bits/types/struct_timeval64.h>
|
||||
|
||||
/* These definitions from linux/timex.h as of 3.18. */
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <sysdep.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <y2038-support.h>
|
||||
#include "kernel-posix-cpu-timers.h"
|
||||
|
||||
#ifdef HAVE_CLOCK_GETRES_VSYSCALL
|
||||
@@ -48,4 +49,64 @@
|
||||
#define SYSDEP_GETRES_CPU SYSCALL_GETRES
|
||||
#define SYSDEP_GETRES_CPUTIME /* Default catches them too. */
|
||||
|
||||
/* The 64-bit version */
|
||||
|
||||
// Call the 32-bit syscall and convert to 64-bit time
|
||||
#define SYSCALL_GETRES32 \
|
||||
retval = INLINE_VSYSCALL (clock_getres, 2, clock_id, &ts32); \
|
||||
if (retval==0) \
|
||||
{ \
|
||||
timespec_to_timespec64(&ts32, res); \
|
||||
res->tv_pad = 0; \
|
||||
}
|
||||
|
||||
#ifdef __NR_clock_getres_time64
|
||||
|
||||
/* We are building with a 64-bit-time getres syscall */
|
||||
|
||||
#define SYSCALL_GETRES64 \
|
||||
if (__y2038_linux_support > 0) \
|
||||
{ \
|
||||
retval = INLINE_SYSCALL (clock_getres_time64, 2, clock_id, res); \
|
||||
if (retval == -1 && errno == ENOSYS) \
|
||||
{ \
|
||||
__y2038_linux_support = -1; \
|
||||
SYSCALL_GETRES32; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
SYSCALL_GETRES32; \
|
||||
} \
|
||||
break
|
||||
|
||||
#else
|
||||
|
||||
/* We are building without a 64-bit-time getres syscall */
|
||||
|
||||
#define SYSCALL_GETRES64 \
|
||||
SYSCALL_GETRES32; \
|
||||
break
|
||||
|
||||
#endif
|
||||
|
||||
/* The REALTIME and MONOTONIC clock are definitely supported in the
|
||||
kernel. */
|
||||
#define SYSDEP_GETRES64 \
|
||||
SYSDEP_GETRES_CPUTIME64 \
|
||||
case CLOCK_REALTIME: \
|
||||
case CLOCK_MONOTONIC: \
|
||||
case CLOCK_MONOTONIC_RAW: \
|
||||
case CLOCK_REALTIME_COARSE: \
|
||||
case CLOCK_MONOTONIC_COARSE: \
|
||||
SYSCALL_GETRES64
|
||||
|
||||
/* We handled the REALTIME clock here. */
|
||||
#define HANDLED_REALTIME64 1
|
||||
#define HANDLED_CPUTIME64 1
|
||||
|
||||
#define SYSDEP_GETRES_CPU64 SYSCALL_GETRES64
|
||||
#define SYSDEP_GETRES_CPUTIME64 \
|
||||
struct timespec ts32;
|
||||
|
||||
#include <sysdeps/posix/clock_getres.c>
|
||||
|
||||
@@ -25,6 +25,41 @@
|
||||
# define HAVE_VSYSCALL
|
||||
#endif
|
||||
#include <sysdep-vdso.h>
|
||||
#include <y2038-support.h>
|
||||
|
||||
# define DO_CLOCK_GETTIME_32 \
|
||||
retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, &ts32); \
|
||||
if (retval == 0) \
|
||||
{ \
|
||||
valid_timespec_to_timespec64 (&ts32, tp); \
|
||||
}
|
||||
|
||||
#ifdef __NR_clock_gettime64
|
||||
|
||||
/* We are building with a 64-bit-time clock_gettime syscall */
|
||||
|
||||
# define DO_CLOCK_GETTIME_64 \
|
||||
if (__y2038_linux_support > 0) \
|
||||
{ \
|
||||
retval = INLINE_SYSCALL (clock_gettime64, 2, clock_id, tp); \
|
||||
if (retval == -1 && errno == ENOSYS) \
|
||||
{ \
|
||||
__y2038_linux_support = -1; \
|
||||
DO_CLOCK_GETTIME_32; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
DO_CLOCK_GETTIME_32; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* We are building without a 64-bit-time clock_gettime syscall */
|
||||
|
||||
# define DO_CLOCK_GETTIME_64 DO_CLOCK_GETTIME_32
|
||||
|
||||
#endif
|
||||
|
||||
/* The REALTIME and MONOTONIC clock are definitely supported in the
|
||||
kernel. */
|
||||
@@ -32,7 +67,7 @@
|
||||
SYSDEP_GETTIME_CPUTIME; \
|
||||
case CLOCK_REALTIME: \
|
||||
case CLOCK_MONOTONIC: \
|
||||
retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, tp); \
|
||||
DO_CLOCK_GETTIME_64; \
|
||||
break
|
||||
|
||||
/* We handled the REALTIME clock here. */
|
||||
@@ -40,8 +75,10 @@
|
||||
#define HANDLED_CPUTIME 1
|
||||
|
||||
#define SYSDEP_GETTIME_CPU(clock_id, tp) \
|
||||
retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, tp); \
|
||||
DO_CLOCK_GETTIME_64; \
|
||||
break
|
||||
#define SYSDEP_GETTIME_CPUTIME /* Default catches them too. */
|
||||
|
||||
#define SYSDEP_GETTIME_CPUTIME \
|
||||
struct timespec ts32
|
||||
|
||||
#include <sysdeps/unix/clock_gettime.c>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <sysdep-cancel.h>
|
||||
#include "kernel-posix-cpu-timers.h"
|
||||
|
||||
|
||||
/* We can simply use the syscall. The CPU clocks are not supported
|
||||
with this function. */
|
||||
int
|
||||
@@ -52,3 +51,51 @@ __clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
|
||||
? INTERNAL_SYSCALL_ERRNO (r, err) : 0);
|
||||
}
|
||||
weak_alias (__clock_nanosleep, clock_nanosleep)
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
/* We don't have a 64-bit-time syscall yet, so just convert arguments
|
||||
* between 64-bit and 32-bit time, and use the 32-bit implementation.
|
||||
*
|
||||
* We could do the reverse and make the 32-bit time implementation a
|
||||
* wrapper around the 64-bit-time implementation, but then 32-bit-time
|
||||
* uses would incur four conversions instead of zero right now.
|
||||
*/
|
||||
int
|
||||
__clock_nanosleep64 (clockid_t clock_id, int flags,
|
||||
const struct __timespec64 *req,
|
||||
struct __timespec64 *rem)
|
||||
{
|
||||
int res;
|
||||
struct timespec req32, rem32, *rem32p = NULL;
|
||||
|
||||
if (req == NULL)
|
||||
{
|
||||
__set_errno (EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (req->tv_sec > INT32_MAX || req->tv_sec < INT32_MIN)
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* For now, use the 32-bit-time implementation above */
|
||||
|
||||
req32.tv_sec = req->tv_sec;
|
||||
req32.tv_nsec = req->tv_nsec;
|
||||
|
||||
if (rem != NULL)
|
||||
rem32p = &rem32;
|
||||
|
||||
res = __clock_nanosleep (clock_id, flags, &req32, rem32p);
|
||||
|
||||
if (res == 0 && rem != NULL)
|
||||
{
|
||||
rem->tv_sec = rem32.tv_sec;
|
||||
rem->tv_nsec = rem32.tv_nsec;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <errno.h>
|
||||
#include <sysdep.h>
|
||||
#include <time.h>
|
||||
#include <y2038-support.h>
|
||||
|
||||
#include "kernel-posix-cpu-timers.h"
|
||||
|
||||
@@ -35,4 +36,61 @@
|
||||
#define SYSDEP_SETTIME_CPU \
|
||||
retval = INLINE_SYSCALL (clock_settime, 2, clock_id, tp)
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
# define DO_CLOCK_SETTIME_32 \
|
||||
if (! fits_in_time_t(tp->tv_sec)) \
|
||||
{ \
|
||||
__set_errno (EOVERFLOW); \
|
||||
retval = -1; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
valid_timespec64_to_timespec(tp, &ts32); \
|
||||
retval = INLINE_SYSCALL (clock_settime, 2, clock_id, &ts32); \
|
||||
}
|
||||
|
||||
#ifdef __NR_clock_settime64
|
||||
|
||||
/* We are building with a 64-bit-time clock_gettime syscall */
|
||||
|
||||
# define DO_CLOCK_SETTIME_64 \
|
||||
if (__y2038_linux_support > 0) \
|
||||
{ \
|
||||
ts64.tv_sec = tp->tv_sec; \
|
||||
ts64.tv_nsec = tp->tv_nsec; \
|
||||
ts64.tv_pad = 0; \
|
||||
retval = INLINE_SYSCALL (clock_settime64, 2, clock_id, &ts64); \
|
||||
if (retval == 1 && errno==ENOSYS) \
|
||||
{ \
|
||||
__y2038_linux_support = -1; \
|
||||
DO_CLOCK_SETTIME_32; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
DO_CLOCK_SETTIME_32; \
|
||||
}
|
||||
|
||||
# define SYSDEP_SETTIME64_CPUTIME \
|
||||
struct __timespec64 ts64; \
|
||||
struct timespec ts32
|
||||
|
||||
#else
|
||||
|
||||
/* We are building without a 64-bit-time clock_gettime syscall */
|
||||
|
||||
# define DO_CLOCK_SETTIME_64 DO_CLOCK_SETTIME_32
|
||||
|
||||
# define SYSDEP_SETTIME64_CPUTIME \
|
||||
struct timespec ts32
|
||||
|
||||
#endif
|
||||
|
||||
#define SYSDEP_SETTIME64 \
|
||||
SYSDEP_SETTIME64_CPUTIME; \
|
||||
case CLOCK_REALTIME: \
|
||||
DO_CLOCK_SETTIME_64; \
|
||||
break
|
||||
|
||||
#include <sysdeps/unix/clock_settime.c>
|
||||
|
||||
@@ -131,6 +131,32 @@ futex_reltimed_wait (unsigned int *futex_word, unsigned int expected,
|
||||
}
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
static __always_inline int
|
||||
futex_reltimed_wait64 (unsigned int *futex_word, unsigned int expected,
|
||||
const struct __timespec64 *reltime, int private)
|
||||
{
|
||||
int err = lll_futex_timed_wait64 (futex_word, expected, reltime,
|
||||
private);
|
||||
switch (err)
|
||||
{
|
||||
case 0:
|
||||
case -EAGAIN:
|
||||
case -EINTR:
|
||||
case -ETIMEDOUT:
|
||||
return -err;
|
||||
|
||||
case -EFAULT: /* Must have been caused by a glibc or application bug. */
|
||||
case -EINVAL: /* Either due to wrong alignment or due to the timeout not
|
||||
being normalized. Must have been caused by a glibc or
|
||||
application bug. */
|
||||
case -ENOSYS: /* Must have been caused by a glibc bug. */
|
||||
/* No other errors are documented at this time. */
|
||||
default:
|
||||
futex_fatal_error ();
|
||||
}
|
||||
}
|
||||
|
||||
/* See sysdeps/nptl/futex-internal.h for details. */
|
||||
static __always_inline int
|
||||
futex_reltimed_wait_cancelable (unsigned int *futex_word,
|
||||
@@ -160,6 +186,37 @@ futex_reltimed_wait_cancelable (unsigned int *futex_word,
|
||||
}
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
static __always_inline int
|
||||
futex_reltimed_wait_cancelable64 (unsigned int *futex_word,
|
||||
unsigned int expected,
|
||||
const struct __timespec64 *reltime,
|
||||
int private)
|
||||
{
|
||||
int oldtype;
|
||||
oldtype = __pthread_enable_asynccancel ();
|
||||
int err = lll_futex_timed_wait64 (futex_word, expected, reltime, private);
|
||||
__pthread_disable_asynccancel (oldtype);
|
||||
switch (err)
|
||||
{
|
||||
case 0:
|
||||
case -EAGAIN:
|
||||
case -EINTR:
|
||||
case -ETIMEDOUT:
|
||||
return -err;
|
||||
|
||||
case -EFAULT: /* Must have been caused by a glibc or application bug. */
|
||||
case -EINVAL: /* Either due to wrong alignment or due to the timeout not
|
||||
being normalized. Must have been caused by a glibc or
|
||||
application bug. */
|
||||
case -ENOSYS: /* Must have been caused by a glibc bug. */
|
||||
/* No other errors are documented at this time. */
|
||||
default:
|
||||
futex_fatal_error ();
|
||||
}
|
||||
}
|
||||
|
||||
/* See sysdeps/nptl/futex-internal.h for details. */
|
||||
static __always_inline int
|
||||
futex_abstimed_wait (unsigned int *futex_word, unsigned int expected,
|
||||
@@ -190,6 +247,36 @@ futex_abstimed_wait (unsigned int *futex_word, unsigned int expected,
|
||||
}
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
static __always_inline int
|
||||
futex_abstimed_wait64 (unsigned int *futex_word, unsigned int expected,
|
||||
const struct __timespec64 *abstime, int private)
|
||||
{
|
||||
/* Work around the fact that the kernel rejects negative timeout values
|
||||
despite them being valid. */
|
||||
if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
|
||||
return ETIMEDOUT;
|
||||
int err = lll_futex_timed_wait_bitset64 (futex_word, expected, abstime,
|
||||
FUTEX_CLOCK_REALTIME, private);
|
||||
switch (err)
|
||||
{
|
||||
case 0:
|
||||
case -EAGAIN:
|
||||
case -EINTR:
|
||||
case -ETIMEDOUT:
|
||||
return -err;
|
||||
|
||||
case -EFAULT: /* Must have been caused by a glibc or application bug. */
|
||||
case -EINVAL: /* Either due to wrong alignment or due to the timeout not
|
||||
being normalized. Must have been caused by a glibc or
|
||||
application bug. */
|
||||
case -ENOSYS: /* Must have been caused by a glibc bug. */
|
||||
/* No other errors are documented at this time. */
|
||||
default:
|
||||
futex_fatal_error ();
|
||||
}
|
||||
}
|
||||
|
||||
/* See sysdeps/nptl/futex-internal.h for details. */
|
||||
static __always_inline int
|
||||
futex_abstimed_wait_cancelable (unsigned int *futex_word,
|
||||
@@ -224,6 +311,42 @@ futex_abstimed_wait_cancelable (unsigned int *futex_word,
|
||||
}
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
static __always_inline int
|
||||
futex_abstimed_wait_cancelable64 (unsigned int *futex_word,
|
||||
unsigned int expected,
|
||||
const struct __timespec64 *abstime,
|
||||
int private)
|
||||
{
|
||||
/* Work around the fact that the kernel rejects negative timeout values
|
||||
despite them being valid. */
|
||||
if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
|
||||
return ETIMEDOUT;
|
||||
int oldtype;
|
||||
oldtype = __pthread_enable_asynccancel ();
|
||||
int err = lll_futex_timed_wait_bitset64 (futex_word, expected, abstime,
|
||||
FUTEX_CLOCK_REALTIME, private);
|
||||
__pthread_disable_asynccancel (oldtype);
|
||||
switch (err)
|
||||
{
|
||||
case 0:
|
||||
case -EAGAIN:
|
||||
case -EINTR:
|
||||
case -ETIMEDOUT:
|
||||
return -err;
|
||||
|
||||
case -EFAULT: /* Must have been caused by a glibc or application bug. */
|
||||
case -EINVAL: /* Either due to wrong alignment or due to the timeout not
|
||||
being normalized. Must have been caused by a glibc or
|
||||
application bug. */
|
||||
case -ENOSYS: /* Must have been caused by a glibc bug. */
|
||||
/* No other errors are documented at this time. */
|
||||
default:
|
||||
futex_fatal_error ();
|
||||
}
|
||||
}
|
||||
|
||||
/* See sysdeps/nptl/futex-internal.h for details. */
|
||||
static __always_inline void
|
||||
futex_wake (unsigned int *futex_word, int processes_to_wake, int private)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sysdep.h>
|
||||
|
||||
#include <y2038-support.h>
|
||||
|
||||
/* Change the access time of the file associated with FD to TSP[0] and
|
||||
the modification time of FILE to TSP[1].
|
||||
@@ -36,3 +36,50 @@ futimens (int fd, const struct timespec tsp[2])
|
||||
/* Avoid implicit array coercion in syscall macros. */
|
||||
return INLINE_SYSCALL (utimensat, 4, fd, NULL, &tsp[0], 0);
|
||||
}
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__futimens64 (int fd, const struct __timespec64 tsp[2])
|
||||
{
|
||||
struct timespec ts32[2];
|
||||
/* Only try and use this syscall if defined by kernel */
|
||||
#ifdef __NR_utimesat_time64
|
||||
struct __timespec64 ts64[2];
|
||||
int res;
|
||||
#endif
|
||||
|
||||
if (fd < 0)
|
||||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EBADF);
|
||||
|
||||
/* Only try and use this syscall if defined by kernel */
|
||||
#ifdef __NR_utimesat_time64
|
||||
if (__y2038_linux_support > 0)
|
||||
{
|
||||
ts64[0].tv_sec = tsp[0].tv_sec;
|
||||
ts64[0].tv_nsec = tsp[0].tv_nsec;
|
||||
ts64[0].tv_pad = 0;
|
||||
ts64[1].tv_sec = tsp[1].tv_sec;
|
||||
ts64[1].tv_nsec = tsp[1].tv_nsec;
|
||||
ts64[1].tv_pad = 0;
|
||||
res = INLINE_SYSCALL (utimensat_time64, 4, fd, NULL, &ts64[0], 0);
|
||||
if (res == 0 || errno != ENOSYS)
|
||||
return res;
|
||||
__y2038_linux_support = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (! timespec64_to_timespec(&tsp[0], &ts32[0]))
|
||||
{
|
||||
__set_errno(EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (! timespec64_to_timespec(&tsp[1], &ts32[1]))
|
||||
{
|
||||
__set_errno(EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return INLINE_SYSCALL (utimensat, 4, fd, NULL, &ts32[0], 0);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <sys/time.h>
|
||||
#include <_itoa.h>
|
||||
#include <fcntl.h>
|
||||
#include <y2038-support.h>
|
||||
|
||||
|
||||
/* Change the access time of the file associated with FD to TVP[0] and
|
||||
@@ -49,3 +50,61 @@ __futimes (int fd, const struct timeval tvp[2])
|
||||
return INLINE_SYSCALL (utimensat, 4, fd, NULL, tvp ? &ts : NULL, 0);
|
||||
}
|
||||
weak_alias (__futimes, futimes)
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__futimes64 (int fd, const struct __timeval64 tvp[2])
|
||||
{
|
||||
struct timespec ts32[2], *ts32p = NULL;
|
||||
/* Only try and use this syscall if defined by kernel */
|
||||
#ifdef __NR_utimensat_time64
|
||||
/* The utimensat system call expects timespec not timeval. */
|
||||
struct __timespec64 ts64[2], *ts64p = NULL;
|
||||
int result;
|
||||
#endif
|
||||
|
||||
/* Only try and use this syscall if defined by kernel */
|
||||
#ifdef __NR_utimensat_time64
|
||||
if (__y2038_linux_support > 0)
|
||||
{
|
||||
if (tvp != NULL)
|
||||
{
|
||||
if (tvp[0].tv_usec < 0 || tvp[0].tv_usec >= 1000000
|
||||
|| tvp[1].tv_usec < 0 || tvp[1].tv_usec >= 1000000)
|
||||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
|
||||
|
||||
ts64[0].tv_sec = tvp[0].tv_sec;
|
||||
ts64[0].tv_nsec = tvp[0].tv_usec * 1000;
|
||||
ts64[0].tv_pad = 0;
|
||||
ts64[1].tv_sec = tvp[1].tv_sec;
|
||||
ts64[1].tv_nsec = tvp[1].tv_usec * 1000;
|
||||
ts64[1].tv_pad = 0;
|
||||
ts64p = ts64;
|
||||
}
|
||||
|
||||
result = INLINE_SYSCALL (utimensat_time64, 4, fd, NULL, ts64p, 0);
|
||||
if (result == 0 || errno != ENOSYS)
|
||||
return result;
|
||||
__y2038_linux_support = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tvp != NULL)
|
||||
{
|
||||
if (tvp[0].tv_usec < 0 || tvp[0].tv_usec >= 1000000
|
||||
|| tvp[1].tv_usec < 0 || tvp[1].tv_usec >= 1000000)
|
||||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
|
||||
|
||||
if (tvp[0].tv_sec > INT_MAX || tvp[1].tv_sec > INT_MAX)
|
||||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
|
||||
|
||||
ts32[0].tv_sec = tvp[0].tv_sec;
|
||||
ts32[0].tv_nsec = tvp[0].tv_usec * 1000;
|
||||
ts32[1].tv_sec = tvp[1].tv_sec;
|
||||
ts32[1].tv_nsec = tvp[1].tv_usec * 1000;
|
||||
ts32p = ts32;
|
||||
}
|
||||
|
||||
return INLINE_SYSCALL (utimensat, 4, fd, NULL, ts32p, 0);
|
||||
}
|
||||
|
||||
@@ -51,3 +51,43 @@ hidden_ver (___fxstat64, __fxstat64)
|
||||
strong_alias (___fxstat64, __fxstat64)
|
||||
hidden_def (__fxstat64)
|
||||
#endif
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__fxstat64_time64 (int vers, int fd, struct __stat64_t64 *buf)
|
||||
{
|
||||
int result;
|
||||
struct stat64 st64;
|
||||
|
||||
result = INLINE_SYSCALL (fstat64, 2, fd, &st64);
|
||||
#if defined _HAVE_STAT64___ST_INO && !__ASSUME_ST_INO_64_BIT
|
||||
if (__builtin_expect (!result, 1) && st64.__st_ino != (__ino_t) st64.st_ino)
|
||||
st64.st_ino = st64.__st_ino;
|
||||
#endif
|
||||
if (!result)
|
||||
{
|
||||
buf->st_dev = st64.st_dev;
|
||||
#if defined _HAVE_STAT64___ST_INO
|
||||
buf->__st_ino = st64.__st_ino;
|
||||
#endif
|
||||
buf->st_mode = st64.st_mode;
|
||||
buf->st_nlink = st64.st_nlink;
|
||||
buf->st_uid = st64.st_uid;
|
||||
buf->st_gid = st64.st_gid;
|
||||
buf->st_rdev = st64.st_rdev;
|
||||
buf->st_size = st64.st_size;
|
||||
buf->st_blksize = st64.st_blksize;
|
||||
|
||||
buf->st_blocks = st64.st_blocks;
|
||||
buf->st_atim.tv_sec = st64.st_atim.tv_sec;
|
||||
buf->st_atim.tv_nsec = st64.st_atim.tv_nsec;
|
||||
buf->st_mtim.tv_sec = st64.st_mtim.tv_sec;
|
||||
buf->st_mtim.tv_nsec = st64.st_mtim.tv_nsec;
|
||||
buf->st_ctim.tv_sec = st64.st_ctim.tv_sec;
|
||||
buf->st_ctim.tv_nsec = st64.st_ctim.tv_nsec;
|
||||
|
||||
buf->st_ino = st64.st_ino;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -45,3 +45,48 @@ __fxstatat64 (int vers, int fd, const char *file, struct stat64 *st, int flag)
|
||||
err));
|
||||
}
|
||||
libc_hidden_def (__fxstatat64)
|
||||
|
||||
/* 64-bit time version */
|
||||
|
||||
int
|
||||
__fxstatat64_time64 (int vers, int fd, const char *file, struct __stat64_t64 *buf, int flag)
|
||||
{
|
||||
if (__glibc_unlikely (vers != _STAT_VER_LINUX))
|
||||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
|
||||
|
||||
int result;
|
||||
struct stat64 st64;
|
||||
INTERNAL_SYSCALL_DECL (err);
|
||||
|
||||
result = INTERNAL_SYSCALL (fstatat64, err, 4, fd, file, &st64, flag);
|
||||
if (!__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 1))
|
||||
{
|
||||
buf->st_dev = st64.st_dev;
|
||||
|
||||
#if defined _HAVE_STAT64___ST_INO
|
||||
buf->__st_ino = st64.__st_ino;
|
||||
#endif
|
||||
buf->st_mode = st64.st_mode;
|
||||
buf->st_nlink = st64.st_nlink;
|
||||
buf->st_uid = st64.st_uid;
|
||||
buf->st_gid = st64.st_gid;
|
||||
buf->st_rdev = st64.st_rdev;
|
||||
buf->st_size = st64.st_size;
|
||||
buf->st_blksize = st64.st_blksize;
|
||||
|
||||
buf->st_blocks = st64.st_blocks;
|
||||
buf->st_atim.tv_sec = st64.st_atim.tv_sec;
|
||||
buf->st_atim.tv_nsec = st64.st_atim.tv_nsec;
|
||||
buf->st_mtim.tv_sec = st64.st_mtim.tv_sec;
|
||||
buf->st_mtim.tv_nsec = st64.st_mtim.tv_nsec;
|
||||
buf->st_ctim.tv_sec = st64.st_ctim.tv_sec;
|
||||
buf->st_ctim.tv_nsec = st64.st_ctim.tv_nsec;
|
||||
|
||||
buf->st_ino = st64.st_ino;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (INTERNAL_SYSCALL_ERRNO (result,
|
||||
err));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (C) 2015-2018 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#undef __gettimeofday
|
||||
|
||||
#ifdef HAVE_GETTIMEOFDAY_VSYSCALL
|
||||
# define HAVE_VSYSCALL
|
||||
#include <sysdep-vdso.h>
|
||||
#endif
|
||||
|
||||
/* Get the current time of day and timezone information,
|
||||
putting it into *tv and *tz. If tz is null, *tz is not filled.
|
||||
Returns 0 on success, -1 on errors. */
|
||||
|
||||
int
|
||||
__gettimeofday64 (struct __timeval64 *tv, struct timezone *tz)
|
||||
{
|
||||
struct timeval tv32;
|
||||
int result;
|
||||
|
||||
#ifdef __vdso_gettimeofday
|
||||
result = INLINE_VSYSCALL (gettimeofday, 2, &tv32, tz);
|
||||
#else
|
||||
result = INLINE_SYSCALL (gettimeofday, 2, &tv32, tz);
|
||||
#endif
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
tv->tv_sec = tv32.tv_sec;
|
||||
tv->tv_usec = tv32.tv_usec;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -154,3 +154,17 @@
|
||||
*/
|
||||
|
||||
#define __ASSUME_CLONE_DEFAULT 1
|
||||
|
||||
/* Support for Y2038-proof times.
|
||||
|
||||
If defined, the underlying kernel always provides a set of syscalls
|
||||
(__NR_* names and numbers) which can handle times beyond Y2038.
|
||||
|
||||
If undefined, whether the underlying kernel provides this set or not
|
||||
must be checked dynamically.
|
||||
|
||||
Definition is commented out until Y2038 syscalls are merged in the
|
||||
kernel.
|
||||
*/
|
||||
|
||||
/* #define __ASSUME_KERNEL_Y2038_SUPPORT 1 */
|
||||
|
||||
@@ -82,6 +82,16 @@
|
||||
__lll_private_flag (FUTEX_WAIT, private), \
|
||||
val, timeout)
|
||||
|
||||
#define lll_futex_timed_wait64(futexp, val, timeout, private) \
|
||||
({ \
|
||||
struct timespec ts; \
|
||||
ts.tv_sec = timeout->tv_sec; \
|
||||
ts.tv_nsec = timeout->tv_nsec; \
|
||||
lll_futex_syscall (4, futexp, \
|
||||
__lll_private_flag (FUTEX_WAIT, private), \
|
||||
val, &ts); \
|
||||
})
|
||||
|
||||
#define lll_futex_timed_wait_bitset(futexp, val, timeout, clockbit, private) \
|
||||
lll_futex_syscall (6, futexp, \
|
||||
__lll_private_flag (FUTEX_WAIT_BITSET | (clockbit), \
|
||||
@@ -89,6 +99,18 @@
|
||||
val, timeout, NULL /* Unused. */, \
|
||||
FUTEX_BITSET_MATCH_ANY)
|
||||
|
||||
#define lll_futex_timed_wait_bitset64(futexp, val, timeout, clockbit, private) \
|
||||
({ \
|
||||
struct timespec ts; \
|
||||
ts.tv_sec = timeout->tv_sec; \
|
||||
ts.tv_nsec = timeout->tv_nsec; \
|
||||
lll_futex_syscall (6, futexp, \
|
||||
__lll_private_flag (FUTEX_WAIT_BITSET | (clockbit), \
|
||||
private), \
|
||||
val, &ts, NULL /* Unused. */, \
|
||||
FUTEX_BITSET_MATCH_ANY); \
|
||||
})
|
||||
|
||||
#define lll_futex_wake(futexp, nr, private) \
|
||||
lll_futex_syscall (4, futexp, \
|
||||
__lll_private_flag (FUTEX_WAKE, private), nr, 0)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user