Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #73918 > unrolled thread

Re: threading.Condition.wait() is not catching SIGTERM

Started byNed Deily <nad@acm.org>
First post2014-07-03 13:13 -0700
Last post2014-07-04 09:28 +1000
Articles 3 — 3 participants

Back to article view | Back to comp.lang.python

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: threading.Condition.wait() is not catching SIGTERM Ned Deily <nad@acm.org> - 2014-07-03 13:13 -0700
    Re: threading.Condition.wait() is not catching SIGTERM Roy Smith <roy@panix.com> - 2014-07-03 16:43 -0400
      Re: threading.Condition.wait() is not catching SIGTERM Cameron Simpson <cs@zip.com.au> - 2014-07-04 09:28 +1000

#73918 — Re: threading.Condition.wait() is not catching SIGTERM

FromNed Deily <nad@acm.org>
Date2014-07-03 13:13 -0700
SubjectRe: threading.Condition.wait() is not catching SIGTERM
Message-ID<mailman.11469.1404418450.18130.python-list@python.org>
In article <17F05A1B-44C8-4F25-AFE9-5DBCFFB9982B@gmail.com>,
> I have the following code which when executed waits to be interrupted by 
> SIGINT, SIGTERM or SIGQUIT. When an object is initialized, it creates a 
> threading.Condition() and acquires() it! The program then registers the 
> signal handlers where notify() and release() is called when the above 
> mentioned signals are received. After registering the signal handlers, it 
> calls wait() on the condition variable and block.
> 
> When I tried to stop the program with Ctrl-C, its did not respond. IOW, the 
> _signal_handler() method did not get called.  

I'm not sure what you are trying to do but your test case seems flawed.  
threading.Condition is designed to be used with multiple threads but 
your test doesn't actually use threads.  If you run your test with a 
reasonably current Python 3 (after changing print to print()), you can 
see that it fails (and why it fails) when interrupting with Ctrl-C:

Waiting to be interrupted!
^CReceived terminate request - signal = 2
Traceback (most recent call last):
  File "b.py", line 30, in <module>
    main()
  File "b.py", line 27, in main
    a.register_and_wait()
  File "b.py", line 22, in register_and_wait
    self._termination_signal.wait()      # control blocks here!
  File 
"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threadin
g.py", line 289, in wait
    waiter.acquire()
  File "b.py", line 13, in _signal_handler
    self._termination_signal.notify()
  File 
"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threadin
g.py", line 339, in notify
    raise RuntimeError("cannot notify on un-acquired lock")
RuntimeError: cannot notify on un-acquired lock

After a quick glance, I'm not sure why Python 2.7 is behaving 
differently, e.g. not raising an error, since both versions of 
Condition.notify have the same test so the difference is elsewhere.  
Feel free to open an issue for not catching the error in 2.7 but you 
should rethink what you are trying to do here.

-- 
 Ned Deily,
 nad@acm.org

[toc] | [next] | [standalone]


#73920

FromRoy Smith <roy@panix.com>
Date2014-07-03 16:43 -0400
Message-ID<roy-F18CDB.16431303072014@news.panix.com>
In reply to#73918
In article <mailman.11469.1404418450.18130.python-list@python.org>,
 Ned Deily <nad@acm.org> wrote:

> In article <17F05A1B-44C8-4F25-AFE9-5DBCFFB9982B@gmail.com>,
> > I have the following code which when executed waits to be interrupted by 
> > SIGINT, SIGTERM or SIGQUIT. When an object is initialized, it creates a 
> > threading.Condition() and acquires() it! The program then registers the 
> > signal handlers where notify() and release() is called when the above 
> > mentioned signals are received. After registering the signal handlers, it 
> > calls wait() on the condition variable and block.
> > 
> > When I tried to stop the program with Ctrl-C, its did not respond. IOW, the 
> > _signal_handler() method did not get called.  
> 
> I'm not sure what you are trying to do but your test case seems flawed.  
> threading.Condition is designed to be used with multiple threads but 
> your test doesn't actually use threads.  If you run your test with a 
> reasonably current Python 3 (after changing print to print()), you can 
> see that it fails (and why it fails) when interrupting with Ctrl-C:
> 
> Waiting to be interrupted!
> ^CReceived terminate request - signal = 2
> Traceback (most recent call last):
>   File "b.py", line 30, in <module>
>     main()
>   File "b.py", line 27, in main 
>     a.register_and_wait()
>   File "b.py", line 22, in register_and_wait
>     self._termination_signal.wait()      # control blocks here!
>   File 
> "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threadin
> g.py", line 289, in wait
>     waiter.acquire()
>   File "b.py", line 13, in _signal_handler
>     self._termination_signal.notify()
>   File 
> "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threadin
> g.py", line 339, in notify
>     raise RuntimeError("cannot notify on un-acquired lock")
> RuntimeError: cannot notify on un-acquired lock
> 
> After a quick glance, I'm not sure why Python 2.7 is behaving 
> differently, e.g. not raising an error, since both versions of 
> Condition.notify have the same test so the difference is elsewhere.  
> Feel free to open an issue for not catching the error in 2.7 but you 
> should rethink what you are trying to do here.

That's not the whole story.  I was playing around with his example this 
morning (Python 2.7.1, OSX, Darwin Kernel Version 11.4.2).  My original 
thought was that maybe the wait() call is holding the GIL and breaking 
it up into two threads would solve that somehow.  So, I tried this:

---------------------------------
from signal import signal, SIGINT, SIGTERM, SIGQUIT
from threading import Condition, Thread, current_thread
import time

class A:
    def __init__(self):
        self._termination_signal = Condition()

    def _signal_handler(self, signum, frame):
        print "Received terminate request - signal = {0}".format(signum,)
        del frame
        return

    def register_and_wait(self):
        t = Thread(target=A.run, args=[self._termination_signal])
        t.start()
        signal(SIGINT, self._signal_handler)
        signal(SIGTERM, self._signal_handler)
        signal(SIGQUIT, self._signal_handler)

    @staticmethod
    def run(ts):
        ts.acquire(blocking=0)
        print "Waiting to be interrupted!"
        ts.wait()      # control blocks here!                                                               
        
print "Notified!!"

def main():
    a = A()
    a.register_and_wait()

if __name__ == "__main__":
    main()
---------------------------------------

I got the same result: _signal_handler() never gets called.  You may be 
right that at the point where notify() gets called, you don't have a 
lock, but that's moot because you never even get to that point.  That's 
the fundamental problem here, and it sure smells like a deadlock.  And 
since it happens even in the non-threaded version, my nose says it's 
somehow GIL related.

I just noticed that the acquire call is documented as taking 
specifically True or False, not generically something truthy or falsey.  
I tried it again with accept(blocking=False) and got the same result.

Hmmm, I just also noticed what I think is a bug in the docs 
(https://docs.python.org/2/library/threading.html).  It says, "If a call 
with blocking set to True would block, return False immediately".  Isn't 
that backwards?  Doesn't that describe the blocking=False behavior?

[toc] | [prev] | [next] | [standalone]


#73928

FromCameron Simpson <cs@zip.com.au>
Date2014-07-04 09:28 +1000
Message-ID<mailman.11475.1404431213.18130.python-list@python.org>
In reply to#73920
On 03Jul2014 16:43, Roy Smith <roy@panix.com> wrote:
[...]
>Hmmm, I just also noticed what I think is a bug in the docs
>(https://docs.python.org/2/library/threading.html).  It says, "If a call
>with blocking set to True would block, return False immediately".  Isn't
>that backwards?  Doesn't that describe the blocking=False behavior?

If you mean this text under Lock.acquire:

   When invoked with the blocking argument set to False, do not block. If a call 
   with blocking set to True would block, return False immediately; otherwise, 
   set the lock to locked and return True.

that pretty clearly (to me) describes blocking=False, by contrasting it with a 
behaviour that would obtain if blocking=True. It is in the clause describing 
"the blocking argument set to False", after all.

Cheers,
Cameron Simpson <cs@zip.com.au>

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web