From: Julian Seward Date: Sat, 26 Apr 2003 20:11:15 +0000 (+0000) Subject: Fix threading problems on glibc-2.3.2 or later. Note this is *not* X-Git-Tag: svn/VALGRIND_2_0_0~200 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4efd705c4ba09f9c406a5fb927786cc62ce77d25;p=thirdparty%2Fvalgrind.git Fix threading problems on glibc-2.3.2 or later. Note this is *not* NPTL support. The behaviour of weak vs strong symbols seems to have changed in glibc-2.3.2. This caused problems in coregrind/vg_intercept.c, wherein strong symbols in vg_libpthread.c were intended to override weak symbols in vg_intercept.c, in order to give alternative thread-safe implementations of some functions, poll(), select(), etc. The change involves moving the nonblocking implementations of poll, etc into vg_intercept.c, renaming them to (eg) VGR_(poll), and routing all calls to poll to VGR_(poll) [dually for other such fns]. This means even single-threaded programs now use these functions, but that doesn't strike me as harmful. MERGE TO STABLE, if it doesn't break anything git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1559 --- diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c index 400af5d001..3db4db5dd1 100644 --- a/coregrind/arch/x86-linux/vg_libpthread.c +++ b/coregrind/arch/x86-linux/vg_libpthread.c @@ -78,8 +78,8 @@ Forwardses. ------------------------------------------------------------------ */ -static void wait_for_fd_to_be_readable_or_erring ( int fd ); -static void wait_for_fd_to_be_writable_or_erring ( int fd ); +#define WEAK __attribute__((weak)) + static int my_do_syscall1 ( int syscallno, int arg1 ); @@ -350,7 +350,7 @@ int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit) return 0; } -__attribute__((weak)) +WEAK int pthread_attr_setstacksize (pthread_attr_t *__attr, size_t __stacksize) { @@ -452,7 +452,7 @@ int pthread_getattr_np (pthread_t thread, pthread_attr_t *attr) /* Bogus ... */ -__attribute__((weak)) +WEAK int pthread_attr_getstackaddr ( const pthread_attr_t * attr, void ** stackaddr ) { @@ -464,7 +464,7 @@ int pthread_attr_getstackaddr ( const pthread_attr_t * attr, } /* Not bogus (!) */ -__attribute__((weak)) +WEAK int pthread_attr_getstacksize ( const pthread_attr_t * _attr, size_t * __stacksize ) { @@ -495,7 +495,7 @@ int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) /* This is completely bogus. We reject all attempts to change it from VKI_BYTES_PER_PAGE. I don't have a clue what it's for so it seems safest to be paranoid. */ -__attribute__((weak)) +WEAK int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) { static int moans = N_MOANS; @@ -510,7 +510,7 @@ int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) } /* A straight copy of the LinuxThreads code. */ -__attribute__((weak)) +WEAK int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) { *guardsize = attr->__guardsize; @@ -521,7 +521,7 @@ int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) static int concurrency_current_level = 0; -__attribute__((weak)) +WEAK int pthread_setconcurrency(int new_level) { if (new_level < 0) @@ -532,7 +532,7 @@ int pthread_setconcurrency(int new_level) } } -__attribute__((weak)) +WEAK int pthread_getconcurrency(void) { return concurrency_current_level; @@ -678,7 +678,7 @@ void thread_wrapper ( NewThreadInfo* info ) THREADs ------------------------------------------------ */ -__attribute__((weak)) +WEAK int pthread_yield ( void ) { int res; @@ -1566,7 +1566,7 @@ int __pthread_atfork ( void (*prepare)(void), } -__attribute__((weak)) +WEAK void __pthread_initialize ( void ) { ensure_valgrind("__pthread_initialize"); @@ -1775,10 +1775,6 @@ void ** (*__libc_internal_tsd_address) to the corresponding thread-unaware (?) libc routine. ------------------------------------------------------------------ */ -#include -#include -#include - #ifdef GLIBC_2_1 extern int __sigaction @@ -1809,7 +1805,7 @@ extern int __libc_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen); -__attribute__((weak)) +WEAK int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) @@ -1821,7 +1817,7 @@ int connect(int sockfd, extern int __libc_fcntl(int fd, int cmd, long arg); -__attribute__((weak)) +WEAK int fcntl(int fd, int cmd, long arg) { __my_pthread_testcancel(); @@ -1831,7 +1827,7 @@ int fcntl(int fd, int cmd, long arg) extern ssize_t __libc_write(int fd, const void *buf, size_t count); -__attribute__((weak)) +WEAK ssize_t write(int fd, const void *buf, size_t count) { __my_pthread_testcancel(); @@ -1841,7 +1837,7 @@ ssize_t write(int fd, const void *buf, size_t count) extern ssize_t __libc_read(int fd, void *buf, size_t count); -__attribute__((weak)) +WEAK ssize_t read(int fd, void *buf, size_t count) { __my_pthread_testcancel(); @@ -1987,7 +1983,7 @@ static inline int _open(const char *pathname, int flags, mode_t mode, extern int __libc_open64(const char *pathname, int flags, mode_t mode); -/* __attribute__((weak)) */ +/* WEAK */ int open64(const char *pathname, int flags, mode_t mode) { return _open(pathname, flags, mode, __libc_open64); @@ -1995,7 +1991,7 @@ int open64(const char *pathname, int flags, mode_t mode) extern int __libc_open(const char *pathname, int flags, mode_t mode); -/* __attribute__((weak)) */ +/* WEAK */ int open(const char *pathname, int flags, mode_t mode) { return _open(pathname, flags, mode, __libc_open); @@ -2003,7 +1999,7 @@ int open(const char *pathname, int flags, mode_t mode) extern int __libc_close(int fd); -__attribute__((weak)) +WEAK int close(int fd) { __my_pthread_testcancel(); @@ -2011,63 +2007,33 @@ int close(int fd) } -extern -int __libc_accept(int s, struct sockaddr *addr, socklen_t *addrlen); - -int VGL_(accept)(int s, struct sockaddr *addr, socklen_t *addrlen) +WEAK +int accept(int s, struct sockaddr *addr, socklen_t *addrlen) { - __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(s); - __my_pthread_testcancel(); - return __libc_accept(s, addr, addrlen); + return VGR_(accept)(s, addr, addrlen); } -extern -int __libc_recv(int s, void *buf, size_t len, int flags); - -int VGL_(recv)(int s, void *buf, size_t len, int flags) +WEAK +int recv(int s, void *buf, size_t len, int flags) { - __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(s); - __my_pthread_testcancel(); - return __libc_recv(s, buf, len, flags); + return VGR_(recv)(s, buf, len, flags); } -int VGL_(readv)(int fd, const struct iovec *iov, int count) +WEAK +int readv(int fd, const struct iovec *iov, int count) { - int res; - - __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(fd); - __my_pthread_testcancel(); - res = my_do_syscall3(__NR_readv, fd, (unsigned)iov, count); - - if (is_kerror(res)) { - *(__errno_location()) = -res; - return -1; - } - return res; + return VGR_(readv)(fd, iov, count); } -int VGL_(writev)(int fd, struct iovec *iov, int count) +WEAK +int writev(int fd, const struct iovec *iov, int count) { - int res; - - __my_pthread_testcancel(); - wait_for_fd_to_be_writable_or_erring(fd); - __my_pthread_testcancel(); - res = my_do_syscall3(__NR_writev, fd, (unsigned)iov, count); - - if (is_kerror(res)) { - *(__errno_location()) = -res; - return -1; - } - return res; + return VGR_(writev)(fd, iov, count); } extern pid_t __libc_waitpid(pid_t pid, int *status, int options); -__attribute__((weak)) +WEAK pid_t waitpid(pid_t pid, int *status, int options) { __my_pthread_testcancel(); @@ -2077,7 +2043,7 @@ pid_t waitpid(pid_t pid, int *status, int options) extern int __libc_nanosleep(const struct timespec *req, struct timespec *rem); -__attribute__((weak)) +WEAK int nanosleep(const struct timespec *req, struct timespec *rem) { __my_pthread_testcancel(); @@ -2087,7 +2053,7 @@ int nanosleep(const struct timespec *req, struct timespec *rem) extern int __libc_fsync(int fd); -__attribute__((weak)) +WEAK int fsync(int fd) { __my_pthread_testcancel(); @@ -2097,7 +2063,7 @@ int fsync(int fd) extern off_t __libc_lseek(int fildes, off_t offset, int whence); -__attribute__((weak)) +WEAK off_t lseek(int fildes, off_t offset, int whence) { __my_pthread_testcancel(); @@ -2107,7 +2073,7 @@ off_t lseek(int fildes, off_t offset, int whence) extern __off64_t __libc_lseek64(int fildes, __off64_t offset, int whence); -__attribute__((weak)) +WEAK __off64_t lseek64(int fildes, __off64_t offset, int whence) { __my_pthread_testcancel(); @@ -2139,7 +2105,7 @@ ssize_t __pwrite64 (int __fd, const void *__buf, size_t __nbytes, extern ssize_t __libc_pwrite(int fd, const void *buf, size_t count, off_t offset); -__attribute__((weak)) +WEAK ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) { __my_pthread_testcancel(); @@ -2149,7 +2115,7 @@ ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) extern ssize_t __libc_pread(int fd, void *buf, size_t count, off_t offset); -__attribute__((weak)) +WEAK ssize_t pread(int fd, void *buf, size_t count, off_t offset) { __my_pthread_testcancel(); @@ -2159,7 +2125,7 @@ ssize_t pread(int fd, void *buf, size_t count, off_t offset) extern void __libc_longjmp(jmp_buf env, int val) __attribute((noreturn)); -/* not weak: __attribute__((weak)) */ +/* not weak: WEAK */ void longjmp(jmp_buf env, int val) { __libc_longjmp(env, val); @@ -2177,7 +2143,7 @@ void siglongjmp(sigjmp_buf env, int val) extern int __libc_send(int s, const void *msg, size_t len, int flags); -__attribute__((weak)) +WEAK int send(int s, const void *msg, size_t len, int flags) { __my_pthread_testcancel(); @@ -2185,21 +2151,9 @@ int send(int s, const void *msg, size_t len, int flags) } -extern -int __libc_recv(int s, void *buf, size_t len, int flags); -__attribute__((weak)) -int recv(int s, void *buf, size_t len, int flags) -{ - __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(s); - __my_pthread_testcancel(); - return __libc_recv(s, buf, len, flags); -} - - extern int __libc_sendmsg(int s, const struct msghdr *msg, int flags); -__attribute__((weak)) +WEAK int sendmsg(int s, const struct msghdr *msg, int flags) { __my_pthread_testcancel(); @@ -2209,7 +2163,7 @@ int sendmsg(int s, const struct msghdr *msg, int flags) extern int __libc_recvmsg(int s, struct msghdr *msg, int flags); -__attribute__((weak)) +WEAK int recvmsg(int s, struct msghdr *msg, int flags) { __my_pthread_testcancel(); @@ -2220,12 +2174,12 @@ int recvmsg(int s, struct msghdr *msg, int flags) extern int __libc_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); -__attribute__((weak)) +WEAK int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) { __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(s); + VGR_(wait_for_fd_to_be_readable_or_erring)(s); __my_pthread_testcancel(); return __libc_recvfrom(s, buf, len, flags, from, fromlen); } @@ -2234,7 +2188,7 @@ int recvfrom(int s, void *buf, size_t len, int flags, extern int __libc_sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); -__attribute__((weak)) +WEAK int sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) { @@ -2245,7 +2199,7 @@ int sendto(int s, const void *msg, size_t len, int flags, extern int __libc_system(const char* str); -__attribute__((weak)) +WEAK int system(const char* str) { __my_pthread_testcancel(); @@ -2255,7 +2209,7 @@ int system(const char* str) extern pid_t __libc_wait(int *status); -__attribute__((weak)) +WEAK pid_t wait(int *status) { __my_pthread_testcancel(); @@ -2265,7 +2219,7 @@ pid_t wait(int *status) extern int __libc_msync(const void *start, size_t length, int flags); -__attribute__((weak)) +WEAK int msync(const void *start, size_t length, int flags) { __my_pthread_testcancel(); @@ -2352,15 +2306,6 @@ pid_t __vfork(void) } -/* --------------------------------------------------------------------- - Nonblocking implementations of select() and poll(). This stuff will - surely rot your mind. - ------------------------------------------------------------------ */ - -/*--------------------------------------------------*/ - -#include "vg_kerneliface.h" - static int my_do_syscall1 ( int syscallno, int arg1 ) { @@ -2415,342 +2360,18 @@ int my_do_syscall5 ( int syscallno, return __res; } -static -int do_syscall_select( int n, - vki_fd_set* readfds, - vki_fd_set* writefds, - vki_fd_set* exceptfds, - struct vki_timeval * timeout ) -{ - int res; - int args[5]; - args[0] = n; - args[1] = (int)readfds; - args[2] = (int)writefds; - args[3] = (int)exceptfds; - args[4] = (int)timeout; - res = my_do_syscall1(__NR_select, (int)(&(args[0])) ); - return res; -} - -/* This is a wrapper round select(), which makes it thread-safe, - meaning that only this thread will block, rather than the entire - process. This wrapper in turn depends on nanosleep() not to block - the entire process, but I think (hope? suspect?) that POSIX - pthreads guarantees that to be the case. - - Basic idea is: modify the timeout parameter to select so that it - returns immediately. Poll like this until select returns non-zero, - indicating something interesting happened, or until our time is up. - Space out the polls with nanosleeps of say 11 milliseconds, which - is required to be nonblocking; this allows other threads to run. - - Assumes: - * (checked via my_assert) types fd_set and vki_fd_set are identical. - * (checked via my_assert) types timeval and vki_timeval are identical. - * (unchecked) libc error numbers (EINTR etc) are the negation of the - kernel's error numbers (VKI_EINTR etc). -*/ - -int VGL_(select) ( int n, - fd_set *rfds, - fd_set *wfds, - fd_set *xfds, - struct timeval *timeout ) +WEAK +int select ( int n, + fd_set *rfds, + fd_set *wfds, + fd_set *xfds, + struct timeval *timeout ) { - unsigned int ms_now, ms_end; - int res; - fd_set rfds_copy; - fd_set wfds_copy; - fd_set xfds_copy; - struct vki_timeval t_now; - struct vki_timeval zero_timeout; - struct vki_timespec nanosleep_interval; - - __my_pthread_testcancel(); - - /* gcc's complains about ms_end being used uninitialised -- classic - case it can't understand, where ms_end is both defined and used - only if timeout != NULL. Hence ... */ - ms_end = 0; - - /* We assume that the kernel and libc data layouts are identical - for the following types. These asserts provide a crude - check. */ - if (sizeof(fd_set) != sizeof(vki_fd_set) - || sizeof(struct timeval) != sizeof(struct vki_timeval)) - barf("valgrind's hacky non-blocking select(): data sizes error"); - - /* Detect the current time and simultaneously find out if we are - running on Valgrind. */ - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - - /* If a zero timeout specified, this call is harmless. Also go - this route if we're not running on Valgrind, for whatever - reason. */ - if ( (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) - || (ms_now == 0xFFFFFFFF) ) { - res = do_syscall_select( n, (vki_fd_set*)rfds, - (vki_fd_set*)wfds, - (vki_fd_set*)xfds, - (struct vki_timeval*)timeout); - if (is_kerror(res)) { - * (__errno_location()) = -res; - return -1; - } else { - return res; - } - } - - /* If a timeout was specified, set ms_end to be the end millisecond - counter [wallclock] time. */ - if (timeout) { - res = my_do_syscall2(__NR_gettimeofday, (int)&t_now, (int)NULL); - my_assert(res == 0); - ms_end = ms_now; - ms_end += (timeout->tv_usec / 1000); - ms_end += (timeout->tv_sec * 1000); - /* Stay sane ... */ - my_assert (ms_end >= ms_now); - } - - /* fprintf(stderr, "MY_SELECT: before loop\n"); */ - - /* Either timeout == NULL, meaning wait indefinitely, or timeout != - NULL, in which case ms_end holds the end time. */ - - while (1) { - - /* First, do a return-immediately select(). */ - - /* These could be trashed each time round the loop, so restore - them each time. */ - if (rfds) rfds_copy = *rfds; - if (wfds) wfds_copy = *wfds; - if (xfds) xfds_copy = *xfds; - - zero_timeout.tv_sec = zero_timeout.tv_usec = 0; - - res = do_syscall_select( n, - rfds ? (vki_fd_set*)(&rfds_copy) : NULL, - wfds ? (vki_fd_set*)(&wfds_copy) : NULL, - xfds ? (vki_fd_set*)(&xfds_copy) : NULL, - & zero_timeout ); - if (is_kerror(res)) { - /* Some kind of error (including EINTR). Set errno and - return. The sets are unspecified in this case. */ - * (__errno_location()) = -res; - return -1; - } - if (res > 0) { - /* one or more fds is ready. Copy out resulting sets and - return. */ - if (rfds) *rfds = rfds_copy; - if (wfds) *wfds = wfds_copy; - if (xfds) *xfds = xfds_copy; - return res; - } - - /* Nothing interesting happened, so we go to sleep for a - while. */ - - /* fprintf(stderr, "MY_SELECT: nanosleep\n"); */ - /* nanosleep and go round again */ - nanosleep_interval.tv_sec = 0; - nanosleep_interval.tv_nsec = 11 * 1000 * 1000; /* 11 milliseconds */ - /* It's critical here that valgrind's nanosleep implementation - is nonblocking. */ - res = my_do_syscall2(__NR_nanosleep, - (int)(&nanosleep_interval), (int)NULL); - if (res == -VKI_EINTR) { - /* The nanosleep was interrupted by a signal. So we do the - same. */ - * (__errno_location()) = EINTR; - return -1; - } - - /* Sleeping finished. If a finite timeout, check to see if it - has expired yet. */ - if (timeout) { - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - my_assert(ms_now != 0xFFFFFFFF); - if (ms_now >= ms_end) { - /* timeout; nothing interesting happened. */ - if (rfds) FD_ZERO(rfds); - if (wfds) FD_ZERO(wfds); - if (xfds) FD_ZERO(xfds); - return 0; - } - } - - } + return VGR_(select)(n, rfds, wfds, xfds, timeout); } - - -#include - -#ifndef HAVE_NFDS_T -typedef unsigned long int nfds_t; -#endif - - -int VGL_(poll) (struct pollfd *__fds, nfds_t __nfds, int __timeout) -{ - unsigned int ms_now, ms_end; - int res, i; - struct vki_timespec nanosleep_interval; - - __my_pthread_testcancel(); - ensure_valgrind("poll"); - - /* Detect the current time and simultaneously find out if we are - running on Valgrind. */ - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - - if (/* CHECK SIZES FOR struct pollfd */ - sizeof(struct timeval) != sizeof(struct vki_timeval)) - barf("valgrind's hacky non-blocking poll(): data sizes error"); - - /* dummy initialisation to keep gcc -Wall happy */ - ms_end = 0; - - /* If a zero timeout specified, this call is harmless. Also do - this if not running on Valgrind. */ - if (__timeout == 0 || ms_now == 0xFFFFFFFF) { - res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, __timeout); - if (is_kerror(res)) { - * (__errno_location()) = -res; - return -1; - } else { - return res; - } - } - - /* If a timeout was specified, set ms_end to be the end wallclock - time. Easy considering that __timeout is in milliseconds. */ - if (__timeout > 0) { - ms_end = ms_now + (unsigned int)__timeout; - } - - /* fprintf(stderr, "MY_POLL: before loop\n"); */ - - /* Either timeout < 0, meaning wait indefinitely, or timeout > 0, - in which case t_end holds the end time. */ - - my_assert(__timeout != 0); - - while (1) { - - /* Do a return-immediately poll. */ - - res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, 0 ); - if (is_kerror(res)) { - /* Some kind of error. Set errno and return. */ - * (__errno_location()) = -res; - return -1; - } - if (res > 0) { - /* One or more fds is ready. Return now. */ - return res; - } - - /* Nothing interesting happened, so we go to sleep for a - while. */ - - /* fprintf(stderr, "MY_POLL: nanosleep\n"); */ - /* nanosleep and go round again */ - nanosleep_interval.tv_sec = 0; - nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ - /* It's critical here that valgrind's nanosleep implementation - is nonblocking. */ - res = my_do_syscall2(__NR_nanosleep, - (int)(&nanosleep_interval), (int)NULL); - if (res == -VKI_EINTR) { - /* The nanosleep was interrupted by a signal. So we do the - same. */ - * (__errno_location()) = EINTR; - return -1; - } - - /* Sleeping finished. If a finite timeout, check to see if it - has expired yet. */ - if (__timeout > 0) { - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - my_assert(ms_now != 0xFFFFFFFF); - if (ms_now >= ms_end) { - /* timeout; nothing interesting happened. */ - for (i = 0; i < __nfds; i++) - __fds[i].revents = 0; - return 0; - } - } - - } -} - - -/* Helper function used to make accept() non-blocking. Idea is to use - the above nonblocking poll() to make this thread ONLY wait for the - specified fd to become ready, and then return. */ - -/* Sigh -- a hack. We're not supposed to include this file directly; - should do it via /usr/include/fcntl.h, but that introduces a - varargs prototype for fcntl itself, which we can't mimic. */ -#define _FCNTL_H -#include - -static void wait_for_fd_to_be_readable_or_erring ( int fd ) -{ - struct pollfd pfd; - int res; - - /* fprintf(stderr, "wait_for_fd_to_be_readable_or_erring %d\n", fd); */ - - /* First check to see if the fd is nonblocking, and/or invalid. In - either case return immediately. */ - res = __libc_fcntl(fd, F_GETFL, 0); - if (res == -1) return; /* fd is invalid somehow */ - if (res & O_NONBLOCK) return; /* fd is nonblocking */ - - /* Ok, we'd better wait with poll. */ - pfd.fd = fd; - pfd.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; - /* ... but not POLLOUT, you may notice. */ - pfd.revents = 0; - (void)poll(&pfd, 1, -1 /* forever */); -} - -static void wait_for_fd_to_be_writable_or_erring ( int fd ) -{ - struct pollfd pfd; - int res; - - /* fprintf(stderr, "wait_for_fd_to_be_readable_or_erring %d\n", fd); */ - - /* First check to see if the fd is nonblocking, and/or invalid. In - either case return immediately. */ - res = __libc_fcntl(fd, F_GETFL, 0); - if (res == -1) return; /* fd is invalid somehow */ - if (res & O_NONBLOCK) return; /* fd is nonblocking */ - - /* Ok, we'd better wait with poll. */ - pfd.fd = fd; - pfd.events = POLLOUT | POLLERR | POLLHUP | POLLNVAL; - pfd.revents = 0; - (void)poll(&pfd, 1, -1 /* forever */); -} - /* --------------------------------------------------------------------- Hacky implementation of semaphores. ------------------------------------------------------------------ */ @@ -3302,95 +2923,22 @@ pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) /* --------------------------------------------------------------------- - Make SYSV IPC not block everything + Make SYSV IPC not block everything -- pass to vg_intercept.c. ------------------------------------------------------------------ */ -#include -#include -#include /* for ipc_kludge */ - -static inline int sys_ipc(unsigned call, int first, int second, int third, void *ptr) +WEAK +int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg) { - return my_do_syscall5(__NR_ipc, call, first, second, third, (int)ptr); -} - -/* Turn a blocking msgsnd() into a polling non-blocking one, so that - other threads make progress */ -int VGL_(msgsnd)(int msgid, const void *msgp, size_t msgsz, int msgflg) -{ - struct vki_timespec nanosleep_interval; - int err; - - ensure_valgrind("msgsnd"); - - nanosleep_interval.tv_sec = 0; - nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ - - if (msgflg & IPC_NOWAIT) { - /* If we aren't blocking anyway, just do it */ - err = sys_ipc(11, msgid, msgsz, msgflg, (void *)msgp); - } else { - /* Otherwise poll on the queue to let other things run */ - for(;;) { - err = sys_ipc(11, msgid, msgsz, msgflg | IPC_NOWAIT, (void *)msgp); - - if (err != -EAGAIN) - break; - - (void)my_do_syscall2(__NR_nanosleep, - (int)(&nanosleep_interval), (int)NULL); - } - } - - if (is_kerror(err)) { - *(__errno_location()) = -err; - return -1; - } - return 0; + return VGR_(msgsnd)(msgid, msgp, msgsz, msgflg); } -/* Turn a blocking msgrcv() into a polling non-blocking one, so that - other threads make progress */ -int VGL_(msgrcv)( int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg ) +WEAK +int msgrcv(int msqid, void* msgp, size_t msgsz, + long msgtyp, int msgflg ) { - struct vki_timespec nanosleep_interval; - int err; - struct ipc_kludge tmp; - - ensure_valgrind("msgrcv"); - - nanosleep_interval.tv_sec = 0; - nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ - - tmp.msgp = msgp; - tmp.msgtyp = msgtyp; - - if (msgflg & IPC_NOWAIT) { - /* If we aren't blocking anyway, just do it */ - err = sys_ipc(12, msqid, msgsz, msgflg, &tmp ); - } else { - /* Otherwise poll on the queue to let other things run */ - for(;;) { - err = sys_ipc(12, msqid, msgsz, msgflg | IPC_NOWAIT, &tmp ); - - if (err != -ENOMSG) - break; - - (void)my_do_syscall2(__NR_nanosleep, - (int)(&nanosleep_interval), (int)NULL); - } - } - - if (is_kerror(err)) { - *(__errno_location()) = -err; - return -1; - } - - return err; + return VGR_(msgrcv)(msqid, msgp, msgsz, msgtyp, msgflg ); } - - /* --------------------------------------------------------------------- B'stard. ------------------------------------------------------------------ */ diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h index 6ce46899e1..522adfcfba 100644 --- a/coregrind/vg_include.h +++ b/coregrind/vg_include.h @@ -535,6 +535,40 @@ In vg_constants.h: called at program exit. */ extern void VG_(__libc_freeres_wrapper)( void ); +extern void VGR_(wait_for_fd_to_be_readable_or_erring) ( int fd ); +extern void VGR_(wait_for_fd_to_be_writable_or_erring) ( int fd ); + +extern int VGR_(recv)(int s, void *buf, unsigned int/*size_t*/ len, + int flags); + +extern int VGR_(accept)(int s, /*struct sockaddr*/ void *addr, + /*socklen_t*/ void *addrlen); + + +extern int VGR_(select) ( int n, + /*fd_set*/ void *rfds, + /*fd_set*/ void *wfds, + /*fd_set*/ void *xfds, + /*struct timeval*/ void *timeout ); + +extern int VGR_(msgsnd)(int msgid, + const void *msgp, + /*size_t*/ unsigned int msgsz, + int msgflg); + +extern int VGR_(msgrcv)( int msqid, + void* msgp, + /*size_t*/ unsigned int msgsz, + long msgtyp, + int msgflg ); + +extern int VGR_(readv)(int fd, + const /*struct iovec*/ void *iovV, + int count); + +extern int VGR_(writev)(int fd, + const /*struct iovec*/ void *iovV, + int count); /* --------------------------------------------------------------------- Constants pertaining to the simulated CPU state, VG_(baseBlock), @@ -1579,8 +1613,6 @@ extern Int VG_(compact_helper_offsets)[]; extern Addr VG_(noncompact_helper_addrs) []; extern Int VG_(noncompact_helper_offsets)[]; -#define VGL_(x) vgIntercept_##x - #endif /* ndef __VG_INCLUDE_H */ diff --git a/coregrind/vg_intercept.c b/coregrind/vg_intercept.c index ca4c30ff4a..ca7c9abdfb 100644 --- a/coregrind/vg_intercept.c +++ b/coregrind/vg_intercept.c @@ -31,17 +31,20 @@ /* --------------------------------------------------------------------- - All the code in this file runs on the SIMULATED CPU. It is + ALL THE CODE IN THIS FILE RUNS ON THE SIMULATED CPU. It is intended for various reasons as drop-in replacements for libc functions. These functions have global visibility (obviously) and have no prototypes in vg_include.h, since they are not intended to be called from within Valgrind. ------------------------------------------------------------------ */ -/* This has some nasty duplication of stuff from vg_libpthread.c */ - -#include -#include +/* General idea (2003-Apr-26) is that master implementations of + selected functions are done as VGR_(fnname). Then we route + all calls to the master, both here and in vg_libpthread.c. + This means we no longer have to rely on the semantics of weak + symbols, which seems to have changed in glibc >= 2.3.2 in such + a way as to make the previous interception scheme stop working. +*/ /* Sidestep the normal check which disallows using valgrind.h directly. */ @@ -49,29 +52,69 @@ #include "valgrind.h" #include "vg_include.h" +#include "vg_kerneliface.h" + +/* This has some nasty duplication of stuff from vg_libpthread.c */ + +#include +#include +#include +#include +#include +#include /* for ipc_kludge */ +#include +#include +#include + + +/* --------------------------------------------------------------- */ # define strong_alias(name, aliasname) \ extern __typeof (name) aliasname __attribute__ ((alias (#name))); -# define weak_alias(name, aliasname) \ - extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))); - #define WEAK __attribute__((weak)) -#include "vg_kerneliface.h" +/* --------------------------------------------------------------- */ -static -__inline__ -int is_kerror ( int res ) -{ - if (res >= -4095 && res <= -1) - return 1; - else - return 0; +static __inline__ +int my_do_syscall1 ( int syscallno, int arg1 ) +{ + int __res; + __asm__ volatile ("pushl %%ebx; movl %%edx,%%ebx ; int $0x80 ; popl %%ebx" + : "=a" (__res) + : "0" (syscallno), + "d" (arg1) ); + return __res; +} + +static __inline__ +int my_do_syscall2 ( int syscallno, + int arg1, int arg2 ) +{ + int __res; + __asm__ volatile ("pushl %%ebx; movl %%edx,%%ebx ; int $0x80 ; popl %%ebx" + : "=a" (__res) + : "0" (syscallno), + "d" (arg1), + "c" (arg2) ); + return __res; } +static __inline__ +int my_do_syscall3 ( int syscallno, + int arg1, int arg2, int arg3 ) +{ + int __res; + __asm__ volatile ("pushl %%ebx; movl %%esi,%%ebx ; int $0x80 ; popl %%ebx" + : "=a" (__res) + : "0" (syscallno), + "S" (arg1), + "c" (arg2), + "d" (arg3) ); + return __res; +} -static inline +static __inline__ int my_do_syscall5 ( int syscallno, int arg1, int arg2, int arg3, int arg4, int arg5 ) { @@ -87,17 +130,278 @@ int my_do_syscall5 ( int syscallno, return __res; } -/* -------------------------------- msgsnd -------------------------------- */ -#include /* for ipc_kludge */ +static __inline__ +int do_syscall_select( int n, + vki_fd_set* readfds, + vki_fd_set* writefds, + vki_fd_set* exceptfds, + struct vki_timeval * timeout ) +{ + int res; + int args[5]; + args[0] = n; + args[1] = (int)readfds; + args[2] = (int)writefds; + args[3] = (int)exceptfds; + args[4] = (int)timeout; + res = my_do_syscall1(__NR_select, (int)(&(args[0])) ); + return res; +} -static inline int sys_ipc(unsigned call, int first, int second, int third, void *ptr) + +static __inline__ +int do_syscall_ipc( unsigned call, + int first, int second, int third, + void *ptr) { return my_do_syscall5(__NR_ipc, call, first, second, third, (int)ptr); } -WEAK int VGL_(msgsnd)(int msgid, const void *msgp, size_t msgsz, int msgflg) + +/* --------------------------------------------------------------- */ + +/* Just start up Valgrind if it's not already going. VG_(startup)() + detects and ignores second and subsequent calls. */ + +/* We need this guy -- it's in valgrind.so. */ +extern void VG_(startup) ( void ); + +static __inline__ +void ensure_valgrind ( char* caller ) +{ + VG_(startup)(); +} + +static __inline__ +int is_kerror ( int res ) +{ + if (res >= -4095 && res <= -1) + return 1; + else + return 0; +} + +/* --------------------------------------------------------------- */ + +/* Extract from Valgrind the value of VG_(clo_trace_pthread_level). + Returns 0 (none) if not running on Valgrind. */ +static +int get_pt_trace_level ( void ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__GET_PTHREAD_TRACE_LEVEL, + 0, 0, 0, 0); + return res; +} + +static +void cat_n_send ( char* pre, char* msg ) +{ + char buf[1000]; + int res; + if (get_pt_trace_level() >= 0) { + snprintf(buf, sizeof(buf), "%s%s", pre, msg ); + buf[sizeof(buf)-1] = '\0'; + VALGRIND_MAGIC_SEQUENCE(res, 0, /* irrelevant default */ + VG_USERREQ__LOGMESSAGE, buf, 0, 0, 0); + } +} + +static +void my_exit ( int arg ) +{ + my_do_syscall1(__NR_exit, arg); + /*NOTREACHED*/ +} + +static +void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn ) +{ + char buf[1000]; + static Bool entered = False; + if (entered) + my_exit(2); + entered = True; + sprintf(buf, "\n%s: %s:%d (%s): Assertion `%s' failed.\n", + "valgrind", file, line, fn, expr ); + cat_n_send ( "", buf ); + sprintf(buf, "Please report this bug to me at: %s\n\n", + VG_EMAIL_ADDR); + cat_n_send ( "", buf ); + my_exit(1); +} + +#define MY__STRING(__str) #__str + +#define my_assert(expr) \ + ((void) ((expr) ? 0 : \ + (my_assert_fail (MY__STRING(expr), \ + __FILE__, __LINE__, \ + __PRETTY_FUNCTION__), 0))) + +/* --------------------------------------------------------------- */ + +static __inline__ +void __my_pthread_testcancel(void) +{ + int res; + ensure_valgrind("__my_pthread_testcancel"); + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__TESTCANCEL, + 0, 0, 0, 0); + my_assert(res == 0); +} + +/* ================================ poll ================================ */ + +/* This is the master implementation of poll(). It blocks only the + calling thread. All roads lead here. +*/ + +#ifndef HAVE_NFDS_T +typedef unsigned long int nfds_t; +#endif + + +int VGR_(poll) (struct pollfd *__fds, nfds_t __nfds, int __timeout) +{ + unsigned int ms_now, ms_end; + int res, i; + struct vki_timespec nanosleep_interval; + + __my_pthread_testcancel(); + ensure_valgrind("poll"); + + /* Detect the current time and simultaneously find out if we are + running on Valgrind. */ + VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, + VG_USERREQ__READ_MILLISECOND_TIMER, + 0, 0, 0, 0); + + /* CHECK SIZES FOR struct pollfd */ + my_assert(sizeof(struct timeval) == sizeof(struct vki_timeval)); + + /* dummy initialisation to keep gcc -Wall happy */ + ms_end = 0; + + /* If a zero timeout specified, this call is harmless. Also do + this if not running on Valgrind. */ + if (__timeout == 0 || ms_now == 0xFFFFFFFF) { + res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, __timeout); + if (is_kerror(res)) { + * (__errno_location()) = -res; + return -1; + } else { + return res; + } + } + + /* If a timeout was specified, set ms_end to be the end wallclock + time. Easy considering that __timeout is in milliseconds. */ + if (__timeout > 0) { + ms_end = ms_now + (unsigned int)__timeout; + } + + /* fprintf(stderr, "MY_POLL: before loop\n"); */ + + /* Either timeout < 0, meaning wait indefinitely, or timeout > 0, + in which case t_end holds the end time. */ + + my_assert(__timeout != 0); + + while (1) { + + /* Do a return-immediately poll. */ + + res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, 0 ); + if (is_kerror(res)) { + /* Some kind of error. Set errno and return. */ + * (__errno_location()) = -res; + return -1; + } + if (res > 0) { + /* One or more fds is ready. Return now. */ + return res; + } + + /* Nothing interesting happened, so we go to sleep for a + while. */ + + /* fprintf(stderr, "MY_POLL: nanosleep\n"); */ + /* nanosleep and go round again */ + nanosleep_interval.tv_sec = 0; + nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ + /* It's critical here that valgrind's nanosleep implementation + is nonblocking. */ + res = my_do_syscall2(__NR_nanosleep, + (int)(&nanosleep_interval), (int)NULL); + if (res == -VKI_EINTR) { + /* The nanosleep was interrupted by a signal. So we do the + same. */ + * (__errno_location()) = EINTR; + return -1; + } + + /* Sleeping finished. If a finite timeout, check to see if it + has expired yet. */ + if (__timeout > 0) { + VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, + VG_USERREQ__READ_MILLISECOND_TIMER, + 0, 0, 0, 0); + my_assert(ms_now != 0xFFFFFFFF); + if (ms_now >= ms_end) { + /* timeout; nothing interesting happened. */ + for (i = 0; i < __nfds; i++) + __fds[i].revents = 0; + return 0; + } + } + + } +} + +int poll(struct pollfd *__fds, nfds_t __nfds, int __timeout) +{ + return VGR_(poll)(__fds, __nfds, __timeout); +} + +strong_alias(poll, __poll) + +/* ================================ msgsnd ================================ */ + +/* Turn a blocking msgsnd() into a polling non-blocking one, so that + other threads make progress */ +int VGR_(msgsnd)(int msgid, + const void *msgp, + /*size_t*/ unsigned int msgsz, + int msgflg) { - int err = sys_ipc(11, msgid, msgsz, msgflg, (void *)msgp); + struct vki_timespec nanosleep_interval; + int err; + + ensure_valgrind("msgsnd"); + + nanosleep_interval.tv_sec = 0; + nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ + + if (msgflg & IPC_NOWAIT) { + /* If we aren't blocking anyway, just do it */ + err = do_syscall_ipc(11, msgid, msgsz, msgflg, (void *)msgp); + } else { + /* Otherwise poll on the queue to let other things run */ + for(;;) { + err = do_syscall_ipc(11, msgid, msgsz, msgflg | IPC_NOWAIT, + (void *)msgp); + + if (err != -EAGAIN) + break; + + (void)my_do_syscall2(__NR_nanosleep, + (int)(&nanosleep_interval), (int)NULL); + } + } + if (is_kerror(err)) { *(__errno_location()) = -err; return -1; @@ -107,143 +411,259 @@ WEAK int VGL_(msgsnd)(int msgid, const void *msgp, size_t msgsz, int msgflg) int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg) { - return VGL_(msgsnd)(msgid, msgp, msgsz, msgflg); + return VGR_(msgsnd)(msgid, msgp, msgsz, msgflg); } -/* -------------------------------- msgrcv -------------------------------- */ +/* ================================ msgrcv ================================ */ -WEAK int VGL_(msgrcv)( int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg ) +/* Turn a blocking msgrcv() into a polling non-blocking one, so that + other threads make progress */ +int VGR_(msgrcv)( int msqid, + void* msgp, + /*size_t*/ unsigned int msgsz, + long msgtyp, + int msgflg ) { - struct ipc_kludge tmp; + struct vki_timespec nanosleep_interval; int err; + struct ipc_kludge tmp; + + ensure_valgrind("msgrcv"); + + nanosleep_interval.tv_sec = 0; + nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ tmp.msgp = msgp; tmp.msgtyp = msgtyp; - err = sys_ipc(12, msqid, msgsz, msgflg, &tmp ); + if (msgflg & IPC_NOWAIT) { + /* If we aren't blocking anyway, just do it */ + err = do_syscall_ipc(12, msqid, msgsz, msgflg, &tmp ); + } else { + /* Otherwise poll on the queue to let other things run */ + for(;;) { + err = do_syscall_ipc(12, msqid, msgsz, msgflg | IPC_NOWAIT, &tmp ); + + if (err != -ENOMSG) + break; + (void)my_do_syscall2(__NR_nanosleep, + (int)(&nanosleep_interval), (int)NULL); + } + } + if (is_kerror(err)) { *(__errno_location()) = -err; return -1; } + return err; } int msgrcv( int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg ) { - return VGL_(msgrcv)( msqid, msgp, msgsz, msgtyp, msgflg ); + return VGR_(msgrcv)( msqid, msgp, msgsz, msgtyp, msgflg ); } -/* -------------------------------- accept -------------------------------- */ - -#include +/* ================================ accept ================================ */ extern int __libc_accept(int s, struct sockaddr *addr, socklen_t *addrlen); -WEAK int VGL_(accept)(int s, struct sockaddr *addr, socklen_t *addrlen) + +int VGR_(accept)(int s, /*struct sockaddr*/ void *addr, + /*socklen_t*/ void *addrlen) { + __my_pthread_testcancel(); + VGR_(wait_for_fd_to_be_readable_or_erring)(s); + __my_pthread_testcancel(); return __libc_accept(s, addr, addrlen); } int accept(int s, struct sockaddr *addr, socklen_t *addrlen) { - return VGL_(accept)(s, addr, addrlen); + return VGR_(accept)(s, addr, addrlen); } -/* -------------------------------- recv -------------------------------- */ +/* ================================ recv ================================ */ extern int __libc_recv(int s, void *buf, size_t len, int flags); -WEAK int VGL_(recv)(int s, void *buf, size_t len, int flags) +int VGR_(recv)(int s, void *buf, size_t len, int flags) { + __my_pthread_testcancel(); + VGR_(wait_for_fd_to_be_readable_or_erring)(s); + __my_pthread_testcancel(); return __libc_recv(s, buf, len, flags); } int recv(int s, void *buf, size_t len, int flags) { - return VGL_(recv)(s, buf, len, flags); + return VGR_(recv)(s, buf, len, flags); } strong_alias(recv, __recv) -/* -------------------------------- poll -------------------------------- */ - -static inline -int my_do_syscall3 ( int syscallno, - int arg1, int arg2, int arg3 ) -{ - int __res; - __asm__ volatile ("pushl %%ebx; movl %%esi,%%ebx ; int $0x80 ; popl %%ebx" - : "=a" (__res) - : "0" (syscallno), - "S" (arg1), - "c" (arg2), - "d" (arg3) ); - return __res; -} - -#include +/* ================================ select ================================ */ + +/* This is a wrapper round select(), which makes it thread-safe, + meaning that only this thread will block, rather than the entire + process. This wrapper in turn depends on nanosleep() not to block + the entire process, but I think (hope? suspect?) that POSIX + pthreads guarantees that to be the case. + + Basic idea is: modify the timeout parameter to select so that it + returns immediately. Poll like this until select returns non-zero, + indicating something interesting happened, or until our time is up. + Space out the polls with nanosleeps of say 11 milliseconds, which + is required to be nonblocking; this allows other threads to run. + + Assumes: + * (checked via my_assert) types fd_set and vki_fd_set are identical. + * (checked via my_assert) types timeval and vki_timeval are identical. + * (unchecked) libc error numbers (EINTR etc) are the negation of the + kernel's error numbers (VKI_EINTR etc). +*/ -#ifndef HAVE_NFDS_T -typedef unsigned long int nfds_t; -#endif +int VGR_(select) ( int n, + /*fd_set*/ void *rfdsV, + /*fd_set*/ void *wfdsV, + /*fd_set*/ void *xfdsV, + /*struct timeval*/ void *timeoutV ) +{ + unsigned int ms_now, ms_end; + int res; + fd_set rfds_copy; + fd_set wfds_copy; + fd_set xfds_copy; + struct vki_timeval t_now; + struct vki_timeval zero_timeout; + struct vki_timespec nanosleep_interval; + struct timeval* timeout = (struct timeval*)timeoutV; + fd_set* rfds = (fd_set*)rfdsV; + fd_set* wfds = (fd_set*)wfdsV; + fd_set* xfds = (fd_set*)xfdsV; -WEAK int VGL_(poll)(struct pollfd *__fds, nfds_t __nfds, int __timeout) -{ - int res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, __timeout); + __my_pthread_testcancel(); - if (is_kerror(res)) { - * (__errno_location()) = -res; - return -1; - } - return res; -} + /* gcc's complains about ms_end being used uninitialised -- classic + case it can't understand, where ms_end is both defined and used + only if timeout != NULL. Hence ... */ + ms_end = 0; -int poll(struct pollfd *__fds, nfds_t __nfds, int __timeout) -{ - return VGL_(poll)(__fds, __nfds, __timeout); -} + /* We assume that the kernel and libc data layouts are identical + for the following types. These asserts provide a crude + check. */ + my_assert(sizeof(struct timeval) == sizeof(struct vki_timeval)); -strong_alias(poll, __poll) + /* Detect the current time and simultaneously find out if we are + running on Valgrind. */ + VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, + VG_USERREQ__READ_MILLISECOND_TIMER, + 0, 0, 0, 0); + /* If a zero timeout specified, this call is harmless. Also go + this route if we're not running on Valgrind, for whatever + reason. */ + if ( (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) + || (ms_now == 0xFFFFFFFF) ) { + res = do_syscall_select( n, (vki_fd_set*)rfds, + (vki_fd_set*)wfds, + (vki_fd_set*)xfds, + (struct vki_timeval*)timeout); + if (is_kerror(res)) { + * (__errno_location()) = -res; + return -1; + } else { + return res; + } + } -/* -------------------------------- select -------------------------------- */ + /* If a timeout was specified, set ms_end to be the end millisecond + counter [wallclock] time. */ + if (timeout) { + res = my_do_syscall2(__NR_gettimeofday, (int)&t_now, (int)NULL); + my_assert(res == 0); + ms_end = ms_now; + ms_end += (timeout->tv_usec / 1000); + ms_end += (timeout->tv_sec * 1000); + /* Stay sane ... */ + my_assert (ms_end >= ms_now); + } + /* fprintf(stderr, "MY_SELECT: before loop\n"); */ -static inline -int my_do_syscall1 ( int syscallno, int arg1 ) -{ - int __res; - __asm__ volatile ("pushl %%ebx; movl %%edx,%%ebx ; int $0x80 ; popl %%ebx" - : "=a" (__res) - : "0" (syscallno), - "d" (arg1) ); - return __res; -} + /* Either timeout == NULL, meaning wait indefinitely, or timeout != + NULL, in which case ms_end holds the end time. */ + while (1) { -WEAK int VGL_(select)( int n, - fd_set* readfds, - fd_set* writefds, - fd_set* exceptfds, - struct timeval * timeout ) -{ - int res; - int args[5]; - args[0] = n; - args[1] = (int)readfds; - args[2] = (int)writefds; - args[3] = (int)exceptfds; - args[4] = (int)timeout; - res = my_do_syscall1(__NR_select, (int)(&(args[0])) ); + /* First, do a return-immediately select(). */ + + /* These could be trashed each time round the loop, so restore + them each time. */ + if (rfds) rfds_copy = *rfds; + if (wfds) wfds_copy = *wfds; + if (xfds) xfds_copy = *xfds; + + zero_timeout.tv_sec = zero_timeout.tv_usec = 0; + + res = do_syscall_select( n, + rfds ? (vki_fd_set*)(&rfds_copy) : NULL, + wfds ? (vki_fd_set*)(&wfds_copy) : NULL, + xfds ? (vki_fd_set*)(&xfds_copy) : NULL, + & zero_timeout ); + if (is_kerror(res)) { + /* Some kind of error (including EINTR). Set errno and + return. The sets are unspecified in this case. */ + * (__errno_location()) = -res; + return -1; + } + if (res > 0) { + /* one or more fds is ready. Copy out resulting sets and + return. */ + if (rfds) *rfds = rfds_copy; + if (wfds) *wfds = wfds_copy; + if (xfds) *xfds = xfds_copy; + return res; + } + + /* Nothing interesting happened, so we go to sleep for a + while. */ + + /* fprintf(stderr, "MY_SELECT: nanosleep\n"); */ + /* nanosleep and go round again */ + nanosleep_interval.tv_sec = 0; + nanosleep_interval.tv_nsec = 11 * 1000 * 1000; /* 11 milliseconds */ + /* It's critical here that valgrind's nanosleep implementation + is nonblocking. */ + res = my_do_syscall2(__NR_nanosleep, + (int)(&nanosleep_interval), (int)NULL); + if (res == -VKI_EINTR) { + /* The nanosleep was interrupted by a signal. So we do the + same. */ + * (__errno_location()) = EINTR; + return -1; + } + + /* Sleeping finished. If a finite timeout, check to see if it + has expired yet. */ + if (timeout) { + VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, + VG_USERREQ__READ_MILLISECOND_TIMER, + 0, 0, 0, 0); + my_assert(ms_now != 0xFFFFFFFF); + if (ms_now >= ms_end) { + /* timeout; nothing interesting happened. */ + if (rfds) FD_ZERO(rfds); + if (wfds) FD_ZERO(wfds); + if (xfds) FD_ZERO(xfds); + return 0; + } + } - if (is_kerror(res)) { - *(__errno_location()) = -res; - return -1; } - return res; } int select ( int n, @@ -252,55 +672,65 @@ int select ( int n, fd_set *xfds, struct timeval *timeout ) { - return VGL_(select)(n, rfds, wfds, xfds, timeout); + return VGR_(select)(n, rfds, wfds, xfds, timeout); } strong_alias(select, __select) -/* -------------------------------- readv -------------------------------- */ - -#include +/* ================================ readv ================================ */ -WEAK int VGL_(readv)(int fd, const struct iovec *iov, int count) +int VGR_(readv)(int fd, const /*struct iovec*/ void *iovV, int count) { - int res = my_do_syscall3(__NR_readv, fd, (unsigned)iov, count); + int res; + const struct iovec* iov = (const struct iovec*)iovV; + + __my_pthread_testcancel(); + VGR_(wait_for_fd_to_be_readable_or_erring)(fd); + __my_pthread_testcancel(); + res = my_do_syscall3(__NR_readv, fd, (unsigned)iov, count); if (is_kerror(res)) { *(__errno_location()) = -res; return -1; } - return res; } int readv (int fd, const struct iovec *iov, int count) { - return VGL_(readv)(fd, iov, count); + return VGR_(readv)(fd, iov, count); } strong_alias(readv, __readv) -/* -------------------------------- writev -------------------------------- */ +/* ================================ writev ================================ */ -WEAK int VGL_(writev)(int fd, const struct iovec *iov, int count) +int VGR_(writev)(int fd, const /*struct iovec*/ void *iovV, int count) { - int res = my_do_syscall3(__NR_writev, fd, (unsigned)iov, count); + int res; + struct iovec* iov = (struct iovec*)iovV; + + __my_pthread_testcancel(); + VGR_(wait_for_fd_to_be_writable_or_erring)(fd); + __my_pthread_testcancel(); + res = my_do_syscall3(__NR_writev, fd, (unsigned)iov, count); if (is_kerror(res)) { *(__errno_location()) = -res; return -1; } - return res; } int writev (int fd, const struct iovec *iov, int count) { - return VGL_(writev)(fd, iov, count); + return VGR_(writev)(fd, iov, count); } strong_alias(writev, __writev) +/* ================================ sigsuspend ============================ */ + /* --------------------------------------------------------------------- Horrible hack to make sigsuspend() sort-of work OK. Same trick as for pause() in vg_libpthread.so. @@ -378,6 +808,65 @@ Bool VG_(is_running_on_simd_CPU)(void) return VG_(running_on_simd_CPU); } + +/* --------------------------------------------------------------------- + Helpers for safely (nonblockingly) detecting when a file descriptor + is safe to use. + ------------------------------------------------------------------ */ + +/* Helper function used to make accept() non-blocking. Idea is to use + the above nonblocking poll() to make this thread ONLY wait for the + specified fd to become ready, and then return. */ + +/* Sigh -- a hack. We're not supposed to include this file directly; + should do it via /usr/include/fcntl.h, but that introduces a + varargs prototype for fcntl itself, which we can't mimic. */ +#define _FCNTL_H +#include + +extern int __libc_fcntl(int fd, int cmd, long arg); + +void VGR_(wait_for_fd_to_be_readable_or_erring) ( int fd ) +{ + struct pollfd pfd; + int res; + + /* fprintf(stderr, "wait_for_fd_to_be_readable_or_erring %d\n", fd); */ + + /* First check to see if the fd is nonblocking, and/or invalid. In + either case return immediately. */ + res = __libc_fcntl(fd, F_GETFL, 0); + if (res == -1) return; /* fd is invalid somehow */ + if (res & O_NONBLOCK) return; /* fd is nonblocking */ + + /* Ok, we'd better wait with poll. */ + pfd.fd = fd; + pfd.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; + /* ... but not POLLOUT, you may notice. */ + pfd.revents = 0; + (void)poll(&pfd, 1, -1 /* forever */); +} + +void VGR_(wait_for_fd_to_be_writable_or_erring) ( int fd ) +{ + struct pollfd pfd; + int res; + + /* fprintf(stderr, "wait_for_fd_to_be_readable_or_erring %d\n", fd); */ + + /* First check to see if the fd is nonblocking, and/or invalid. In + either case return immediately. */ + res = __libc_fcntl(fd, F_GETFL, 0); + if (res == -1) return; /* fd is invalid somehow */ + if (res & O_NONBLOCK) return; /* fd is nonblocking */ + + /* Ok, we'd better wait with poll. */ + pfd.fd = fd; + pfd.events = POLLOUT | POLLERR | POLLHUP | POLLNVAL; + pfd.revents = 0; + (void)poll(&pfd, 1, -1 /* forever */); +} + /*--------------------------------------------------------------------*/ /*--- end vg_intercept.c ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c index 400af5d001..3db4db5dd1 100644 --- a/coregrind/vg_libpthread.c +++ b/coregrind/vg_libpthread.c @@ -78,8 +78,8 @@ Forwardses. ------------------------------------------------------------------ */ -static void wait_for_fd_to_be_readable_or_erring ( int fd ); -static void wait_for_fd_to_be_writable_or_erring ( int fd ); +#define WEAK __attribute__((weak)) + static int my_do_syscall1 ( int syscallno, int arg1 ); @@ -350,7 +350,7 @@ int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit) return 0; } -__attribute__((weak)) +WEAK int pthread_attr_setstacksize (pthread_attr_t *__attr, size_t __stacksize) { @@ -452,7 +452,7 @@ int pthread_getattr_np (pthread_t thread, pthread_attr_t *attr) /* Bogus ... */ -__attribute__((weak)) +WEAK int pthread_attr_getstackaddr ( const pthread_attr_t * attr, void ** stackaddr ) { @@ -464,7 +464,7 @@ int pthread_attr_getstackaddr ( const pthread_attr_t * attr, } /* Not bogus (!) */ -__attribute__((weak)) +WEAK int pthread_attr_getstacksize ( const pthread_attr_t * _attr, size_t * __stacksize ) { @@ -495,7 +495,7 @@ int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) /* This is completely bogus. We reject all attempts to change it from VKI_BYTES_PER_PAGE. I don't have a clue what it's for so it seems safest to be paranoid. */ -__attribute__((weak)) +WEAK int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) { static int moans = N_MOANS; @@ -510,7 +510,7 @@ int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) } /* A straight copy of the LinuxThreads code. */ -__attribute__((weak)) +WEAK int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) { *guardsize = attr->__guardsize; @@ -521,7 +521,7 @@ int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) static int concurrency_current_level = 0; -__attribute__((weak)) +WEAK int pthread_setconcurrency(int new_level) { if (new_level < 0) @@ -532,7 +532,7 @@ int pthread_setconcurrency(int new_level) } } -__attribute__((weak)) +WEAK int pthread_getconcurrency(void) { return concurrency_current_level; @@ -678,7 +678,7 @@ void thread_wrapper ( NewThreadInfo* info ) THREADs ------------------------------------------------ */ -__attribute__((weak)) +WEAK int pthread_yield ( void ) { int res; @@ -1566,7 +1566,7 @@ int __pthread_atfork ( void (*prepare)(void), } -__attribute__((weak)) +WEAK void __pthread_initialize ( void ) { ensure_valgrind("__pthread_initialize"); @@ -1775,10 +1775,6 @@ void ** (*__libc_internal_tsd_address) to the corresponding thread-unaware (?) libc routine. ------------------------------------------------------------------ */ -#include -#include -#include - #ifdef GLIBC_2_1 extern int __sigaction @@ -1809,7 +1805,7 @@ extern int __libc_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen); -__attribute__((weak)) +WEAK int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) @@ -1821,7 +1817,7 @@ int connect(int sockfd, extern int __libc_fcntl(int fd, int cmd, long arg); -__attribute__((weak)) +WEAK int fcntl(int fd, int cmd, long arg) { __my_pthread_testcancel(); @@ -1831,7 +1827,7 @@ int fcntl(int fd, int cmd, long arg) extern ssize_t __libc_write(int fd, const void *buf, size_t count); -__attribute__((weak)) +WEAK ssize_t write(int fd, const void *buf, size_t count) { __my_pthread_testcancel(); @@ -1841,7 +1837,7 @@ ssize_t write(int fd, const void *buf, size_t count) extern ssize_t __libc_read(int fd, void *buf, size_t count); -__attribute__((weak)) +WEAK ssize_t read(int fd, void *buf, size_t count) { __my_pthread_testcancel(); @@ -1987,7 +1983,7 @@ static inline int _open(const char *pathname, int flags, mode_t mode, extern int __libc_open64(const char *pathname, int flags, mode_t mode); -/* __attribute__((weak)) */ +/* WEAK */ int open64(const char *pathname, int flags, mode_t mode) { return _open(pathname, flags, mode, __libc_open64); @@ -1995,7 +1991,7 @@ int open64(const char *pathname, int flags, mode_t mode) extern int __libc_open(const char *pathname, int flags, mode_t mode); -/* __attribute__((weak)) */ +/* WEAK */ int open(const char *pathname, int flags, mode_t mode) { return _open(pathname, flags, mode, __libc_open); @@ -2003,7 +1999,7 @@ int open(const char *pathname, int flags, mode_t mode) extern int __libc_close(int fd); -__attribute__((weak)) +WEAK int close(int fd) { __my_pthread_testcancel(); @@ -2011,63 +2007,33 @@ int close(int fd) } -extern -int __libc_accept(int s, struct sockaddr *addr, socklen_t *addrlen); - -int VGL_(accept)(int s, struct sockaddr *addr, socklen_t *addrlen) +WEAK +int accept(int s, struct sockaddr *addr, socklen_t *addrlen) { - __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(s); - __my_pthread_testcancel(); - return __libc_accept(s, addr, addrlen); + return VGR_(accept)(s, addr, addrlen); } -extern -int __libc_recv(int s, void *buf, size_t len, int flags); - -int VGL_(recv)(int s, void *buf, size_t len, int flags) +WEAK +int recv(int s, void *buf, size_t len, int flags) { - __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(s); - __my_pthread_testcancel(); - return __libc_recv(s, buf, len, flags); + return VGR_(recv)(s, buf, len, flags); } -int VGL_(readv)(int fd, const struct iovec *iov, int count) +WEAK +int readv(int fd, const struct iovec *iov, int count) { - int res; - - __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(fd); - __my_pthread_testcancel(); - res = my_do_syscall3(__NR_readv, fd, (unsigned)iov, count); - - if (is_kerror(res)) { - *(__errno_location()) = -res; - return -1; - } - return res; + return VGR_(readv)(fd, iov, count); } -int VGL_(writev)(int fd, struct iovec *iov, int count) +WEAK +int writev(int fd, const struct iovec *iov, int count) { - int res; - - __my_pthread_testcancel(); - wait_for_fd_to_be_writable_or_erring(fd); - __my_pthread_testcancel(); - res = my_do_syscall3(__NR_writev, fd, (unsigned)iov, count); - - if (is_kerror(res)) { - *(__errno_location()) = -res; - return -1; - } - return res; + return VGR_(writev)(fd, iov, count); } extern pid_t __libc_waitpid(pid_t pid, int *status, int options); -__attribute__((weak)) +WEAK pid_t waitpid(pid_t pid, int *status, int options) { __my_pthread_testcancel(); @@ -2077,7 +2043,7 @@ pid_t waitpid(pid_t pid, int *status, int options) extern int __libc_nanosleep(const struct timespec *req, struct timespec *rem); -__attribute__((weak)) +WEAK int nanosleep(const struct timespec *req, struct timespec *rem) { __my_pthread_testcancel(); @@ -2087,7 +2053,7 @@ int nanosleep(const struct timespec *req, struct timespec *rem) extern int __libc_fsync(int fd); -__attribute__((weak)) +WEAK int fsync(int fd) { __my_pthread_testcancel(); @@ -2097,7 +2063,7 @@ int fsync(int fd) extern off_t __libc_lseek(int fildes, off_t offset, int whence); -__attribute__((weak)) +WEAK off_t lseek(int fildes, off_t offset, int whence) { __my_pthread_testcancel(); @@ -2107,7 +2073,7 @@ off_t lseek(int fildes, off_t offset, int whence) extern __off64_t __libc_lseek64(int fildes, __off64_t offset, int whence); -__attribute__((weak)) +WEAK __off64_t lseek64(int fildes, __off64_t offset, int whence) { __my_pthread_testcancel(); @@ -2139,7 +2105,7 @@ ssize_t __pwrite64 (int __fd, const void *__buf, size_t __nbytes, extern ssize_t __libc_pwrite(int fd, const void *buf, size_t count, off_t offset); -__attribute__((weak)) +WEAK ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) { __my_pthread_testcancel(); @@ -2149,7 +2115,7 @@ ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) extern ssize_t __libc_pread(int fd, void *buf, size_t count, off_t offset); -__attribute__((weak)) +WEAK ssize_t pread(int fd, void *buf, size_t count, off_t offset) { __my_pthread_testcancel(); @@ -2159,7 +2125,7 @@ ssize_t pread(int fd, void *buf, size_t count, off_t offset) extern void __libc_longjmp(jmp_buf env, int val) __attribute((noreturn)); -/* not weak: __attribute__((weak)) */ +/* not weak: WEAK */ void longjmp(jmp_buf env, int val) { __libc_longjmp(env, val); @@ -2177,7 +2143,7 @@ void siglongjmp(sigjmp_buf env, int val) extern int __libc_send(int s, const void *msg, size_t len, int flags); -__attribute__((weak)) +WEAK int send(int s, const void *msg, size_t len, int flags) { __my_pthread_testcancel(); @@ -2185,21 +2151,9 @@ int send(int s, const void *msg, size_t len, int flags) } -extern -int __libc_recv(int s, void *buf, size_t len, int flags); -__attribute__((weak)) -int recv(int s, void *buf, size_t len, int flags) -{ - __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(s); - __my_pthread_testcancel(); - return __libc_recv(s, buf, len, flags); -} - - extern int __libc_sendmsg(int s, const struct msghdr *msg, int flags); -__attribute__((weak)) +WEAK int sendmsg(int s, const struct msghdr *msg, int flags) { __my_pthread_testcancel(); @@ -2209,7 +2163,7 @@ int sendmsg(int s, const struct msghdr *msg, int flags) extern int __libc_recvmsg(int s, struct msghdr *msg, int flags); -__attribute__((weak)) +WEAK int recvmsg(int s, struct msghdr *msg, int flags) { __my_pthread_testcancel(); @@ -2220,12 +2174,12 @@ int recvmsg(int s, struct msghdr *msg, int flags) extern int __libc_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); -__attribute__((weak)) +WEAK int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) { __my_pthread_testcancel(); - wait_for_fd_to_be_readable_or_erring(s); + VGR_(wait_for_fd_to_be_readable_or_erring)(s); __my_pthread_testcancel(); return __libc_recvfrom(s, buf, len, flags, from, fromlen); } @@ -2234,7 +2188,7 @@ int recvfrom(int s, void *buf, size_t len, int flags, extern int __libc_sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); -__attribute__((weak)) +WEAK int sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) { @@ -2245,7 +2199,7 @@ int sendto(int s, const void *msg, size_t len, int flags, extern int __libc_system(const char* str); -__attribute__((weak)) +WEAK int system(const char* str) { __my_pthread_testcancel(); @@ -2255,7 +2209,7 @@ int system(const char* str) extern pid_t __libc_wait(int *status); -__attribute__((weak)) +WEAK pid_t wait(int *status) { __my_pthread_testcancel(); @@ -2265,7 +2219,7 @@ pid_t wait(int *status) extern int __libc_msync(const void *start, size_t length, int flags); -__attribute__((weak)) +WEAK int msync(const void *start, size_t length, int flags) { __my_pthread_testcancel(); @@ -2352,15 +2306,6 @@ pid_t __vfork(void) } -/* --------------------------------------------------------------------- - Nonblocking implementations of select() and poll(). This stuff will - surely rot your mind. - ------------------------------------------------------------------ */ - -/*--------------------------------------------------*/ - -#include "vg_kerneliface.h" - static int my_do_syscall1 ( int syscallno, int arg1 ) { @@ -2415,342 +2360,18 @@ int my_do_syscall5 ( int syscallno, return __res; } -static -int do_syscall_select( int n, - vki_fd_set* readfds, - vki_fd_set* writefds, - vki_fd_set* exceptfds, - struct vki_timeval * timeout ) -{ - int res; - int args[5]; - args[0] = n; - args[1] = (int)readfds; - args[2] = (int)writefds; - args[3] = (int)exceptfds; - args[4] = (int)timeout; - res = my_do_syscall1(__NR_select, (int)(&(args[0])) ); - return res; -} - -/* This is a wrapper round select(), which makes it thread-safe, - meaning that only this thread will block, rather than the entire - process. This wrapper in turn depends on nanosleep() not to block - the entire process, but I think (hope? suspect?) that POSIX - pthreads guarantees that to be the case. - - Basic idea is: modify the timeout parameter to select so that it - returns immediately. Poll like this until select returns non-zero, - indicating something interesting happened, or until our time is up. - Space out the polls with nanosleeps of say 11 milliseconds, which - is required to be nonblocking; this allows other threads to run. - - Assumes: - * (checked via my_assert) types fd_set and vki_fd_set are identical. - * (checked via my_assert) types timeval and vki_timeval are identical. - * (unchecked) libc error numbers (EINTR etc) are the negation of the - kernel's error numbers (VKI_EINTR etc). -*/ - -int VGL_(select) ( int n, - fd_set *rfds, - fd_set *wfds, - fd_set *xfds, - struct timeval *timeout ) +WEAK +int select ( int n, + fd_set *rfds, + fd_set *wfds, + fd_set *xfds, + struct timeval *timeout ) { - unsigned int ms_now, ms_end; - int res; - fd_set rfds_copy; - fd_set wfds_copy; - fd_set xfds_copy; - struct vki_timeval t_now; - struct vki_timeval zero_timeout; - struct vki_timespec nanosleep_interval; - - __my_pthread_testcancel(); - - /* gcc's complains about ms_end being used uninitialised -- classic - case it can't understand, where ms_end is both defined and used - only if timeout != NULL. Hence ... */ - ms_end = 0; - - /* We assume that the kernel and libc data layouts are identical - for the following types. These asserts provide a crude - check. */ - if (sizeof(fd_set) != sizeof(vki_fd_set) - || sizeof(struct timeval) != sizeof(struct vki_timeval)) - barf("valgrind's hacky non-blocking select(): data sizes error"); - - /* Detect the current time and simultaneously find out if we are - running on Valgrind. */ - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - - /* If a zero timeout specified, this call is harmless. Also go - this route if we're not running on Valgrind, for whatever - reason. */ - if ( (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) - || (ms_now == 0xFFFFFFFF) ) { - res = do_syscall_select( n, (vki_fd_set*)rfds, - (vki_fd_set*)wfds, - (vki_fd_set*)xfds, - (struct vki_timeval*)timeout); - if (is_kerror(res)) { - * (__errno_location()) = -res; - return -1; - } else { - return res; - } - } - - /* If a timeout was specified, set ms_end to be the end millisecond - counter [wallclock] time. */ - if (timeout) { - res = my_do_syscall2(__NR_gettimeofday, (int)&t_now, (int)NULL); - my_assert(res == 0); - ms_end = ms_now; - ms_end += (timeout->tv_usec / 1000); - ms_end += (timeout->tv_sec * 1000); - /* Stay sane ... */ - my_assert (ms_end >= ms_now); - } - - /* fprintf(stderr, "MY_SELECT: before loop\n"); */ - - /* Either timeout == NULL, meaning wait indefinitely, or timeout != - NULL, in which case ms_end holds the end time. */ - - while (1) { - - /* First, do a return-immediately select(). */ - - /* These could be trashed each time round the loop, so restore - them each time. */ - if (rfds) rfds_copy = *rfds; - if (wfds) wfds_copy = *wfds; - if (xfds) xfds_copy = *xfds; - - zero_timeout.tv_sec = zero_timeout.tv_usec = 0; - - res = do_syscall_select( n, - rfds ? (vki_fd_set*)(&rfds_copy) : NULL, - wfds ? (vki_fd_set*)(&wfds_copy) : NULL, - xfds ? (vki_fd_set*)(&xfds_copy) : NULL, - & zero_timeout ); - if (is_kerror(res)) { - /* Some kind of error (including EINTR). Set errno and - return. The sets are unspecified in this case. */ - * (__errno_location()) = -res; - return -1; - } - if (res > 0) { - /* one or more fds is ready. Copy out resulting sets and - return. */ - if (rfds) *rfds = rfds_copy; - if (wfds) *wfds = wfds_copy; - if (xfds) *xfds = xfds_copy; - return res; - } - - /* Nothing interesting happened, so we go to sleep for a - while. */ - - /* fprintf(stderr, "MY_SELECT: nanosleep\n"); */ - /* nanosleep and go round again */ - nanosleep_interval.tv_sec = 0; - nanosleep_interval.tv_nsec = 11 * 1000 * 1000; /* 11 milliseconds */ - /* It's critical here that valgrind's nanosleep implementation - is nonblocking. */ - res = my_do_syscall2(__NR_nanosleep, - (int)(&nanosleep_interval), (int)NULL); - if (res == -VKI_EINTR) { - /* The nanosleep was interrupted by a signal. So we do the - same. */ - * (__errno_location()) = EINTR; - return -1; - } - - /* Sleeping finished. If a finite timeout, check to see if it - has expired yet. */ - if (timeout) { - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - my_assert(ms_now != 0xFFFFFFFF); - if (ms_now >= ms_end) { - /* timeout; nothing interesting happened. */ - if (rfds) FD_ZERO(rfds); - if (wfds) FD_ZERO(wfds); - if (xfds) FD_ZERO(xfds); - return 0; - } - } - - } + return VGR_(select)(n, rfds, wfds, xfds, timeout); } - - -#include - -#ifndef HAVE_NFDS_T -typedef unsigned long int nfds_t; -#endif - - -int VGL_(poll) (struct pollfd *__fds, nfds_t __nfds, int __timeout) -{ - unsigned int ms_now, ms_end; - int res, i; - struct vki_timespec nanosleep_interval; - - __my_pthread_testcancel(); - ensure_valgrind("poll"); - - /* Detect the current time and simultaneously find out if we are - running on Valgrind. */ - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - - if (/* CHECK SIZES FOR struct pollfd */ - sizeof(struct timeval) != sizeof(struct vki_timeval)) - barf("valgrind's hacky non-blocking poll(): data sizes error"); - - /* dummy initialisation to keep gcc -Wall happy */ - ms_end = 0; - - /* If a zero timeout specified, this call is harmless. Also do - this if not running on Valgrind. */ - if (__timeout == 0 || ms_now == 0xFFFFFFFF) { - res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, __timeout); - if (is_kerror(res)) { - * (__errno_location()) = -res; - return -1; - } else { - return res; - } - } - - /* If a timeout was specified, set ms_end to be the end wallclock - time. Easy considering that __timeout is in milliseconds. */ - if (__timeout > 0) { - ms_end = ms_now + (unsigned int)__timeout; - } - - /* fprintf(stderr, "MY_POLL: before loop\n"); */ - - /* Either timeout < 0, meaning wait indefinitely, or timeout > 0, - in which case t_end holds the end time. */ - - my_assert(__timeout != 0); - - while (1) { - - /* Do a return-immediately poll. */ - - res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, 0 ); - if (is_kerror(res)) { - /* Some kind of error. Set errno and return. */ - * (__errno_location()) = -res; - return -1; - } - if (res > 0) { - /* One or more fds is ready. Return now. */ - return res; - } - - /* Nothing interesting happened, so we go to sleep for a - while. */ - - /* fprintf(stderr, "MY_POLL: nanosleep\n"); */ - /* nanosleep and go round again */ - nanosleep_interval.tv_sec = 0; - nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ - /* It's critical here that valgrind's nanosleep implementation - is nonblocking. */ - res = my_do_syscall2(__NR_nanosleep, - (int)(&nanosleep_interval), (int)NULL); - if (res == -VKI_EINTR) { - /* The nanosleep was interrupted by a signal. So we do the - same. */ - * (__errno_location()) = EINTR; - return -1; - } - - /* Sleeping finished. If a finite timeout, check to see if it - has expired yet. */ - if (__timeout > 0) { - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - my_assert(ms_now != 0xFFFFFFFF); - if (ms_now >= ms_end) { - /* timeout; nothing interesting happened. */ - for (i = 0; i < __nfds; i++) - __fds[i].revents = 0; - return 0; - } - } - - } -} - - -/* Helper function used to make accept() non-blocking. Idea is to use - the above nonblocking poll() to make this thread ONLY wait for the - specified fd to become ready, and then return. */ - -/* Sigh -- a hack. We're not supposed to include this file directly; - should do it via /usr/include/fcntl.h, but that introduces a - varargs prototype for fcntl itself, which we can't mimic. */ -#define _FCNTL_H -#include - -static void wait_for_fd_to_be_readable_or_erring ( int fd ) -{ - struct pollfd pfd; - int res; - - /* fprintf(stderr, "wait_for_fd_to_be_readable_or_erring %d\n", fd); */ - - /* First check to see if the fd is nonblocking, and/or invalid. In - either case return immediately. */ - res = __libc_fcntl(fd, F_GETFL, 0); - if (res == -1) return; /* fd is invalid somehow */ - if (res & O_NONBLOCK) return; /* fd is nonblocking */ - - /* Ok, we'd better wait with poll. */ - pfd.fd = fd; - pfd.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; - /* ... but not POLLOUT, you may notice. */ - pfd.revents = 0; - (void)poll(&pfd, 1, -1 /* forever */); -} - -static void wait_for_fd_to_be_writable_or_erring ( int fd ) -{ - struct pollfd pfd; - int res; - - /* fprintf(stderr, "wait_for_fd_to_be_readable_or_erring %d\n", fd); */ - - /* First check to see if the fd is nonblocking, and/or invalid. In - either case return immediately. */ - res = __libc_fcntl(fd, F_GETFL, 0); - if (res == -1) return; /* fd is invalid somehow */ - if (res & O_NONBLOCK) return; /* fd is nonblocking */ - - /* Ok, we'd better wait with poll. */ - pfd.fd = fd; - pfd.events = POLLOUT | POLLERR | POLLHUP | POLLNVAL; - pfd.revents = 0; - (void)poll(&pfd, 1, -1 /* forever */); -} - /* --------------------------------------------------------------------- Hacky implementation of semaphores. ------------------------------------------------------------------ */ @@ -3302,95 +2923,22 @@ pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) /* --------------------------------------------------------------------- - Make SYSV IPC not block everything + Make SYSV IPC not block everything -- pass to vg_intercept.c. ------------------------------------------------------------------ */ -#include -#include -#include /* for ipc_kludge */ - -static inline int sys_ipc(unsigned call, int first, int second, int third, void *ptr) +WEAK +int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg) { - return my_do_syscall5(__NR_ipc, call, first, second, third, (int)ptr); -} - -/* Turn a blocking msgsnd() into a polling non-blocking one, so that - other threads make progress */ -int VGL_(msgsnd)(int msgid, const void *msgp, size_t msgsz, int msgflg) -{ - struct vki_timespec nanosleep_interval; - int err; - - ensure_valgrind("msgsnd"); - - nanosleep_interval.tv_sec = 0; - nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ - - if (msgflg & IPC_NOWAIT) { - /* If we aren't blocking anyway, just do it */ - err = sys_ipc(11, msgid, msgsz, msgflg, (void *)msgp); - } else { - /* Otherwise poll on the queue to let other things run */ - for(;;) { - err = sys_ipc(11, msgid, msgsz, msgflg | IPC_NOWAIT, (void *)msgp); - - if (err != -EAGAIN) - break; - - (void)my_do_syscall2(__NR_nanosleep, - (int)(&nanosleep_interval), (int)NULL); - } - } - - if (is_kerror(err)) { - *(__errno_location()) = -err; - return -1; - } - return 0; + return VGR_(msgsnd)(msgid, msgp, msgsz, msgflg); } -/* Turn a blocking msgrcv() into a polling non-blocking one, so that - other threads make progress */ -int VGL_(msgrcv)( int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg ) +WEAK +int msgrcv(int msqid, void* msgp, size_t msgsz, + long msgtyp, int msgflg ) { - struct vki_timespec nanosleep_interval; - int err; - struct ipc_kludge tmp; - - ensure_valgrind("msgrcv"); - - nanosleep_interval.tv_sec = 0; - nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ - - tmp.msgp = msgp; - tmp.msgtyp = msgtyp; - - if (msgflg & IPC_NOWAIT) { - /* If we aren't blocking anyway, just do it */ - err = sys_ipc(12, msqid, msgsz, msgflg, &tmp ); - } else { - /* Otherwise poll on the queue to let other things run */ - for(;;) { - err = sys_ipc(12, msqid, msgsz, msgflg | IPC_NOWAIT, &tmp ); - - if (err != -ENOMSG) - break; - - (void)my_do_syscall2(__NR_nanosleep, - (int)(&nanosleep_interval), (int)NULL); - } - } - - if (is_kerror(err)) { - *(__errno_location()) = -err; - return -1; - } - - return err; + return VGR_(msgrcv)(msqid, msgp, msgsz, msgtyp, msgflg ); } - - /* --------------------------------------------------------------------- B'stard. ------------------------------------------------------------------ */ diff --git a/include/vg_constants_skin.h b/include/vg_constants_skin.h index 2b3fc8d616..534782198f 100644 --- a/include/vg_constants_skin.h +++ b/include/vg_constants_skin.h @@ -44,6 +44,7 @@ #define VG_(str) VGAPPEND(vgPlain_,str) #define VGP_(str) VGAPPEND(vgProf_,str) #define VGOFF_(str) VGAPPEND(vgOff_,str) +#define VGR_(str) VGAPPEND(vgAllRoadsLeadToRome_,str) /* Skin specific ones. Note that final name still starts with "vg". */ #define SK_(str) VGAPPEND(vgSkin_,str)