This is the mail archive of the
cygwin-patches
mailing list for the Cygwin project.
patch: sleep/nanosleep bug
- From: Eric Blake <ebb9 at byu dot net>
- To: cygwin-patches at cygwin dot com
- Date: Wed, 18 Nov 2009 13:13:53 -0700
- Subject: patch: sleep/nanosleep bug
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I see no reason why we can't sleep for more than 49.7 days (not that I
expect many programs to try that, though). Plus, sleep was reading
uninitialized memory, giving garbage answers.
Here's an example that proves where this matters:
$ (time timeout 2 sleep 49d) 2>&1 | grep sys
sys 0m0.046s
$ (time timeout 2 sleep 50d) 2>&1 | grep sys
sys 0m1.500s
Notice how CPU utilization spikes from nearly 0% to nearly 100% once you
cross 49.7 days, because sleep(1) is now in a super-tight loop of
repeatedly calling nanosleep (which fails), then checking the current time
to see if enough elapsed time has occurred.
2009-11-18 Eric Blake <ebb9@byu.net>
* signal.cc (nanosleep): Support 'infinite' sleep times.
(sleep): Avoid uninitialized memory.
- --
Don't work too hard, make some time for fun as well!
Eric Blake ebb9@byu.net
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAksEVYEACgkQ84KuGfSFAYCcFgCfU2rkcgCAQ4Ywfv73mJe51AbL
xZsAnRwuA/ybkXvz+3uEQvUzyHjbWk4l
=pH2l
-----END PGP SIGNATURE-----
diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc
index b3654de..4f248bf 100644
--- a/winsup/cygwin/signal.cc
+++ b/winsup/cygwin/signal.cc
@@ -1,7 +1,7 @@
/* signal.cc
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006, 2007, 2008 Red Hat, Inc.
+ 2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
Written by Steve Chamberlain of Cygnus Support, sac@cygnus.com
Significant changes by Sergey Okhapkin <sos@prospect.com.ru>
@@ -87,14 +87,40 @@ nanosleep (const struct timespec *rqtp, struct timespec *rmtp)
sig_dispatch_pending ();
pthread_testcancel ();
- if ((unsigned int) rqtp->tv_sec > (HIRES_DELAY_MAX / 1000 - 1)
- || (unsigned int) rqtp->tv_nsec > 999999999)
+ if ((unsigned int) rqtp->tv_nsec > 999999999)
{
set_errno (EINVAL);
return -1;
}
+ /* FIXME - needs help if we ever decide to support 64-bit time_t. */
+ time_t sec = rqtp->tv_sec;
+ while ((unsigned int) sec > (HIRES_DELAY_MAX / 1000 - 1))
+ {
+ /* Too big for a single transaction. Repeatedly sleep for
+ smaller chunks (49.7 days at a time!) until either we get
+ EINTR or we have slept as long as the user requested
+ (supposing the universe hasn't burned out yet). */
+ struct timespec temp = { HIRES_DELAY_MAX / 1000 - 1, 0 };
+ int result = nanosleep (&temp, &temp);
+ sec -= HIRES_DELAY_MAX / 1000 - 1;
+ if (result)
+ {
+ if (rmtp)
+ {
+ rmtp->tv_sec = sec + temp.tv_sec;
+ rmtp->tv_nsec = rqtp->tv_nsec + temp.tv_nsec;
+ if (rmtp->tv_nsec >= 1000000000)
+ {
+ rmtp->tv_sec++;
+ rmtp->tv_nsec -= 1000000000;
+ }
+ }
+ return result;
+ }
+ }
+
DWORD resolution = gtod.resolution ();
- DWORD req = ((rqtp->tv_sec * 1000 + (rqtp->tv_nsec + 999999) / 1000000
+ DWORD req = ((sec * 1000 + (rqtp->tv_nsec + 999999) / 1000000
+ resolution - 1) / resolution) * resolution;
DWORD end_time = gtod.dmsecs () + req;
syscall_printf ("nanosleep (%ld)", req);
@@ -126,8 +152,9 @@ sleep (unsigned int seconds)
struct timespec req, rem;
req.tv_sec = seconds;
req.tv_nsec = 0;
- nanosleep (&req, &rem);
- return rem.tv_sec + (rem.tv_nsec > 0);
+ if (nanosleep (&req, &rem))
+ return rem.tv_sec + (rem.tv_nsec > 0);
+ return 0;
}
extern "C" unsigned int
@@ -136,7 +163,7 @@ usleep (useconds_t useconds)
struct timespec req;
req.tv_sec = useconds / 1000000;
req.tv_nsec = (useconds % 1000000) * 1000;
- int res = nanosleep (&req, 0);
+ int res = nanosleep (&req, NULL);
return res;
}
--
1.6.4.2