Skip to content

Commit

Permalink
Fix delayed deadlock when transcript thread dies (#1)
Browse files Browse the repository at this point in the history
Test plan:

- mount a small tmpfs with 'sudo mount -t tmpfs none /mnt -o size=4096'

- create a zdaemon config file
    <runner>
      program yes
      transcript /mnt/transcript.log
    </runner>

- run zdaemon -C conf start

- wait a few milliseconds for /mnt to fill up

- pgrep yes

If the 'yes' program is still running, we have a deadlock (strace and
you'll find it blocked on write()).  This is the situation before this
patch, as described in bug #1.

If the 'yes' program is dead, the deadlock is fixed.

- run zdaemon -C conf status

The daemon manager should be stopped (it's not functional without the
dead transcript thread).

Automating this test is left as an exercise for the reader.  :(
  • Loading branch information
mgedmin committed Apr 15, 2015
1 parent ba102a4 commit 353bc34
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ Change log
manager was waiting before respawning a crashed program.
https://github.com/zopefoundation/zdaemon/issues/13.

- Fix delayed deadlock when the transcript file runs into a full disk
(https://github.com/zopefoundation/zdaemon/issues/1).


4.0.1 (2014-12-26)
==================
Expand Down
22 changes: 14 additions & 8 deletions src/zdaemon/zdrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,14 +625,20 @@ def __init__(self, filename):
thread.start()

def copy(self):
lock = self.lock
i = [self.read_from]
o = e = []
while 1:
ii, oo, ee = select.select(i, o, e)
with lock:
for fd in ii:
self.write(os.read(fd, 8192))
try:
lock = self.lock
i = [self.read_from]
o = e = []
while 1:
ii, oo, ee = select.select(i, o, e)
with lock:
for fd in ii:
self.write(os.read(fd, 8192))
finally:
# since there's no reader from this pipe we want the other side to
# get a SIGPIPE as soon as it tries to write to it, instead of
# deadlocking when the pipe buffer becomes full.
os.close(self.read_from)

def reopen(self):
new_file = open(self.filename, 'ab', 0)
Expand Down

0 comments on commit 353bc34

Please sign in to comment.