[Python-bugs-list] [ python-Bugs-581232 ] [Windows] Can not interrupt time.sleep()

noreply@sourceforge.net noreply@sourceforge.net
Mon, 15 Jul 2002 17:44:45 -0700


Bugs item #581232, was opened at 2002-07-14 03:59
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=581232&group_id=5470

Category: Python Library
Group: Platform-specific
Status: Open
Resolution: Accepted
Priority: 5
Submitted By: Mark Hammond (mhammond)
>Assigned to: Mark Hammond (mhammond)
Summary: [Windows] Can not interrupt time.sleep()

Initial Comment:
As nagged by Tim - time.sleep(10000) can not be
interrupted by Ctrl+C on Windows.  Attaching a patch to
fix this.

----------------------------------------------------------------------

>Comment By: Tim Peters (tim_one)
Date: 2002-07-15 20:44

Message:
Logged In: YES 
user_id=31435

The code looks fine!  I'll try it after it's checked in <wink>.  
(Seriously, it's such a clear change from the last version I'm 
not worried enough to bother.)

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2002-07-15 19:44

Message:
Logged In: YES 
user_id=14198

New patch that only interrupts the main thread.  Tim, can
you please approve/comment?

I have Misc/NEWS blurbs ready to go, but included this bug
in the description so will get this sorted first.

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2002-07-15 09:17

Message:
Logged In: YES 
user_id=14198

Meant to mention: my win98 box shows my original patch (no
Sleep() change) works exactly the same as my 2k box - Ctrl+C
reliably (for 10 attempts <wink>) delivers IOError to the
secondary threads and KeyboardInterrupt to the main thread.

Ironically, for the script I posted to python-dev, python
2.2 on my win98 box works exactly the same as my Linux CVS
version, while my patch makes Windows Python behave like my
Linux Python 1.5.2

does-anyone-really-care? <0.1 wink> ly

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2002-07-15 03:59

Message:
Logged In: YES 
user_id=14198

Sorry about the code mixup.

My code is working as intended by me - we just need to
determine if it is working as intended by anyone else <wink>

Running sleeper.py, the main thread sees a
KeyboardInterrupt, and the other threads all see IOError. 
As Tim says, sleeper.py will deadlock in this case, but
KeyboardInterrupt is still pending on the main thread.

Thus, my change makes sleeper.py work exactly as my Linux
1.5.2 build (where each thread is interrupted).  Under CVS
Linux, the other threads sleep()s are never interrupted.

I will take this to python-dev

----------------------------------------------------------------------

Comment By: Tim Peters (tim_one)
Date: 2002-07-14 23:48

Message:
Logged In: YES 
user_id=31435

Mark, we're running different code.  I'm talking about 
sleeper.py, which I attached to this bug report a few msgs 
back.  See the file attachment whose name is sleeper.py 
<wink>.  It doesn't use threading.py at all, just raw threads 
and raw locks from the thread module.  See that file and 
what I've been talking about will be clear.

About boosting the Sleep() argument, sure, that's 
reasonable.

I don't know anything about Linux quirks.  The design of the 
Windows gimmick here is such that any number of threads 
can wake up in the WaitForSingleObject().  If Linux happens 
to deliver SIGINT only to the main thread, then I suppose 
only the main thread will get KeyboardInterrupt on Linux.  
But I don't know what Linux does -- we'll have to suck a 
Linux geek in fo find out.

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2002-07-14 22:07

Message:
Logged In: YES 
user_id=14198

While there is no sleep long enough to guarantee anything,
we could make it more likely <wink>.  How about we add an
order of magnitude, and sleep for 10ms?  There is no way
this could be considered time critical code.
    
I'm a little confused by your statement:
> (when a thread in sleeper.py is killed via IOError, it
never releases its lock, 
> and the main thread should hang trying to acquire it)

threading.py, Thread.__bootstrap(), via __stop() seems to
ensure that the lock is released in the face of
exceptions.  I don't see how a failing thread (IOError or
otherwise) deadlocks when joining on it.


Re Linux etc.  I didn't realize I was using Python 1.5.2!! 
With that version I saw:
sleeping
Thread 2051 dieing
Exception in thread Thread-2:
Traceback (innermost last):
...
  File "delme.py", line 5, in sleeper
    time.sleep(30)
IOError: [Errno 4] Interrupted system call

Thread 1026 dieing
Exception in thread Thread-1:
Traceback (innermost last):
...
  File "delme.py", line 5, in sleeper
    time.sleep(30)
IOError: [Errno 4] Interrupted system call

waiting
Traceback (innermost last):
  File "delme.py", line 17, in ?
    time.sleep(30)
KeyboardInterrupt

However, with CVS Python on Linux, I see this:

sleeping
waiting
Thread 1026 dieing
Thread 2051 dieing
Traceback (most recent call last):
  File "/home/skip/temp/delme.py", line 18, in ?
    time.sleep(30)
KeyboardInterrupt

[Ignore line number differences!]

NOTE: This only happens *after* the thread's sleep() calls
have expired.  ie, Ctrl+C does *not* appear to interrupt
each thread any more, just the main thread.  Further,
neither 1.5.2 nor CVS Python seem to deadlock at exit
regardless of how often I hit Ctrl+C.

sigh - I am not sure what the intent for Linux is any more
:(  So my specific questions:
    
* In the face of this information, is my patch still
reasonable?  Should we clarify the "most desirable"
semantics (assuming the platform is capable) first?
* Should this apparent linux change be brought up?


----------------------------------------------------------------------

Comment By: Tim Peters (tim_one)
Date: 2002-07-14 21:09

Message:
Logged In: YES 
user_id=31435

Well, there's no Sleep on earth long enough to guarantee 
that some other specific thread got a timeslice, so maybe 
that's the best that can be done.

I'm not sure what you mean when you say that the main 
thread sees KeyboardInterrupt on Linux.  In sleeper.py?  I 
expect the main thread to be hung, waiting to acquire a 
lock that will never be released (when a thread in 
sleeper.py is killed via IOError, it never releases its lock, 
and the main thread should hang trying to acquire it).  A 
lock.acquire() shouldn't be interruptible by Ctrl+C, so 
there's a Linux bug here if the main thread isn't hung.

Anyway, that's irrelevant to this patch, so Accepted and 
back to you.  Reformat so that lines don't go beyond 
column 79?  Also please write some Misc/NEWS blurbs for 
the things you checked in today.  Thanks!

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2002-07-14 19:21

Message:
Logged In: YES 
user_id=14198

I think we are faced with a timing problem for signals.  As
far as I can tell, this follows the same code path as other
platforms.

* Interrupted sleep detected.
* Errno of EINTR set.
* PyErr_SetFromErrNo handles EINTR as a special case - if
Py_CheckSignals() returns TRUE, it leaves KeyboardInterrupt
current and discards the current exception.

So, what this means is that *one* interrupted thread will
see the KeyboardInterrupt exception and all others will see
OSError(EINTR).

Indeed, this is exactly what I see for Linux - the main
thread sees the KeyboardInterrupt, while all other threads
see OSError(EINTR).

My guess is that the Sleep(1) I added is not long enough -
that on Win9x the signal handler has not yet been called,
therefore all threads see OSError.  Is this possible?



----------------------------------------------------------------------

Comment By: Tim Peters (tim_one)
Date: 2002-07-14 19:09

Message:
Logged In: YES 
user_id=31435

This definitely makes time.sleep() interruptible on Win98, 
but I don't think it's quite there yet.  Playing with it by 
hand, one time I saw

IOError: [Errno 4] Interrupted function call

instead of KeyboardInterrupt when hitting Ctrl+C in an 
interactive shell.  The attached sleeper.py provokes lots of 
those by using threads (hit Ctrl+C within 10 seconds of 
starting it).

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2002-07-14 10:46

Message:
Logged In: YES 
user_id=14198

... need uninterrupted sleep <wink>.  Please ignore the
Ctrl+Break comments - I had my bugs mixed up.  Rest is still
valid tho.

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2002-07-14 10:38

Message:
Logged In: YES 
user_id=14198

Here we go - a better patch.  This delivers the exact same
multi-threaded semantics as my Linux build.

Re Ctrl+Break - no argument from me - my "if deemed broken"
was meant to imply that!  Win9x Ctrl+Break must be the same
as Win2k - I seemed to recall on Win9x it behaved like a
Ctrl+C.  No 9x even booted to check, and I don't really care
<wink>

FWIW, my test script:
import time, threading, thread

def sleeper():
    try:
        time.sleep(30)
    finally:
        msg = "Thread %d dieing" % (thread.get_ident(),)
        print msg

threads=[]
for i in range(2):
    t=threading.Thread(target=sleeper)
    t.start()
    threads.append(t)
print "sleeping"
try:
    time.sleep(30)
finally:
    print "waiting"
    [t.join for t in threads]
    print "final short sleep"
    time.sleep(2)


----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2002-07-14 04:05

Message:
Logged In: YES 
user_id=14198

Poo - not a thread-safe patch.  I will think about this some
more tomorrow.

----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=581232&group_id=5470