mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-31 16:36:20 +00:00
exit when stdin closes
This commit is contained in:
parent
7c25e1533f
commit
488a04cb9b
@ -20,7 +20,9 @@ from allmydata.scripts.common import BasedirOptions
|
||||
from twisted.scripts import twistd
|
||||
from twisted.python import usage
|
||||
from twisted.python.reflect import namedAny
|
||||
from twisted.internet.defer import maybeDeferred
|
||||
from twisted.internet.defer import maybeDeferred, Deferred
|
||||
from twisted.internet.protocol import Protocol
|
||||
from twisted.internet.stdio import StandardIO
|
||||
from twisted.application.service import Service
|
||||
|
||||
from allmydata.scripts.default_nodedir import _default_nodedir
|
||||
@ -148,6 +150,8 @@ class DaemonizeTheRealService(Service, HookMixin):
|
||||
|
||||
def startService(self):
|
||||
|
||||
from twisted.internet import reactor
|
||||
|
||||
def start():
|
||||
node_to_instance = {
|
||||
u"client": lambda: maybeDeferred(namedAny("allmydata.client.create_client"), self.basedir),
|
||||
@ -187,12 +191,14 @@ class DaemonizeTheRealService(Service, HookMixin):
|
||||
|
||||
def created(srv):
|
||||
srv.setServiceParent(self.parent)
|
||||
# exiting on stdin-closed facilitates cleanup when run
|
||||
# as a subprocess
|
||||
on_stdin_close(reactor, reactor.stop)
|
||||
d.addCallback(created)
|
||||
d.addErrback(handle_config_error)
|
||||
d.addBoth(self._call_hook, 'running')
|
||||
return d
|
||||
|
||||
from twisted.internet import reactor
|
||||
reactor.callWhenRunning(start)
|
||||
|
||||
|
||||
@ -206,6 +212,43 @@ class DaemonizeTahoeNodePlugin(object):
|
||||
return DaemonizeTheRealService(self.nodetype, self.basedir, so)
|
||||
|
||||
|
||||
def on_stdin_close(reactor, fn):
|
||||
"""
|
||||
Arrange for the function `fn` to run when our stdin closes
|
||||
"""
|
||||
when_closed_d = Deferred()
|
||||
|
||||
class WhenClosed(Protocol):
|
||||
"""
|
||||
Notify a Deferred when our connection is lost .. as this is passed
|
||||
to twisted's StandardIO class, it is used to detect our parent
|
||||
going away.
|
||||
"""
|
||||
|
||||
def connectionLost(self, reason):
|
||||
when_closed_d.callback(None)
|
||||
|
||||
def on_close(arg):
|
||||
try:
|
||||
fn()
|
||||
except Exception:
|
||||
# for our "exit" use-case, this will _mostly_ just be
|
||||
# ReactorNotRunning (because we're already shutting down
|
||||
# when our stdin closes) but no matter what "bad thing"
|
||||
# happens we just want to ignore it.
|
||||
pass
|
||||
return arg
|
||||
|
||||
when_closed_d.addBoth(on_close)
|
||||
# we don't need to do anything with this instance because it gets
|
||||
# hooked into the reactor and thus remembered
|
||||
StandardIO(
|
||||
proto=WhenClosed(),
|
||||
reactor=reactor,
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def run(config, runApp=twistd.runApp):
|
||||
"""
|
||||
Runs a Tahoe-LAFS node in the foreground.
|
||||
|
@ -46,6 +46,9 @@ from twisted.internet.defer import (
|
||||
inlineCallbacks,
|
||||
DeferredList,
|
||||
)
|
||||
from twisted.internet.testing import (
|
||||
MemoryReactorClock,
|
||||
)
|
||||
from twisted.python.filepath import FilePath
|
||||
from twisted.python.runtime import (
|
||||
platform,
|
||||
@ -57,6 +60,9 @@ import allmydata
|
||||
from allmydata.scripts.runner import (
|
||||
parse_options,
|
||||
)
|
||||
from allmydata.scripts.tahoe_run import (
|
||||
on_stdin_close,
|
||||
)
|
||||
|
||||
from .common import (
|
||||
PIPE,
|
||||
@ -621,3 +627,46 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin):
|
||||
# What's left is a perfect indicator that the process has exited and
|
||||
# we won't get blamed for leaving the reactor dirty.
|
||||
yield client_running
|
||||
|
||||
|
||||
class OnStdinCloseTests(SyncTestCase):
|
||||
"""
|
||||
Tests for on_stdin_close
|
||||
"""
|
||||
|
||||
def test_close_called(self):
|
||||
"""
|
||||
our on-close method is called when stdin closes
|
||||
"""
|
||||
reactor = MemoryReactorClock()
|
||||
called = []
|
||||
|
||||
def onclose():
|
||||
called.append(True)
|
||||
on_stdin_close(reactor, onclose)
|
||||
self.assertEqual(called, [])
|
||||
|
||||
reader = list(reactor.readers)[0]
|
||||
reader.loseConnection()
|
||||
reactor.advance(1) # ProcessReader does a callLater(0, ..)
|
||||
|
||||
self.assertEqual(called, [True])
|
||||
|
||||
def test_exception_ignored(self):
|
||||
"""
|
||||
an exception from or on-close function is ignored
|
||||
"""
|
||||
reactor = MemoryReactorClock()
|
||||
called = []
|
||||
|
||||
def onclose():
|
||||
called.append(True)
|
||||
raise RuntimeError("unexpected error")
|
||||
on_stdin_close(reactor, onclose)
|
||||
self.assertEqual(called, [])
|
||||
|
||||
reader = list(reactor.readers)[0]
|
||||
reader.loseConnection()
|
||||
reactor.advance(1) # ProcessReader does a callLater(0, ..)
|
||||
|
||||
self.assertEqual(called, [True])
|
||||
|
Loading…
x
Reference in New Issue
Block a user