macapp: report failure of node startup to the user
authorrobk-tahoe <robk-tahoe@allmydata.com>
Thu, 6 Mar 2008 20:53:21 +0000 (13:53 -0700)
committerrobk-tahoe <robk-tahoe@allmydata.com>
Thu, 6 Mar 2008 20:53:21 +0000 (13:53 -0700)
in certain cases (e.g. the node.pem changed but old .furls are in private/)
the node will abort upon startup. previously it used os.abort() which in these
cases caused the mac gui app to crash on startup with no explanation.

this changes that behaviour from calling os.abort() to calling
node._abort_process(failure) which by default calls os.abort().  this allows
that method to be overridden in subclasses.

the mac app now provides and uses such a subclass of Client, so that failures
are reported to the user in a message dialog before the process exits.
this uses wx.PostEvent() with a custom event type to signal from the reactor
thread into the gui thread.

src/allmydata/gui/macapp.py
src/allmydata/node.py

index 58b470a32743712b88586ce94919af4240067c76..34176b640489f880b6c25362fe0b451cca64314f 100644 (file)
@@ -43,6 +43,23 @@ def run_macapp():
     app = App(basedir)
     return app.run()
 
+ABORT_EVENT_ID = wx.NewId()
+
+class AbortEvent(wx.PyEvent):
+    def __init__(self, failure):
+        wx.PyEvent.__init__(self)
+        self.SetEventType(ABORT_EVENT_ID)
+        self.failure = failure
+
+class MacGuiClient(client.Client):
+    def __init__(self, basedir, app):
+        self.app = app
+        client.Client.__init__(self, basedir)
+
+    def _abort_process(self, failure):
+        event = AbortEvent(failure)
+        wx.PostEvent(self.app.guiapp.frame, event)
+
 class App(object):
     def __init__(self, basedir):
         self.basedir = basedir
@@ -89,8 +106,8 @@ class App(object):
         self.start_reactor()
 
         try:
-            guiapp = MacGuiApp(app=self)
-            guiapp.MainLoop()
+            self.guiapp = MacGuiApp(app=self)
+            self.guiapp.MainLoop()
             log.msg('gui mainloop exited')
         except:
             log.err()
@@ -105,7 +122,8 @@ class App(object):
 
     def launch_reactor(self):
         # run the node itself
-        c = client.Client(self.basedir)
+        #c = client.Client(self.basedir)
+        c = MacGuiClient(self.basedir, self)
         reactor.callLater(0, c.startService) # after reactor startup
         reactor.run(installSignalHandlers=False)
         self.reactor_shutdown.set()
@@ -176,8 +194,9 @@ ACCOUNT_PAGE_ID = wx.NewId()
 MOUNT_ID = wx.NewId()
 
 class SplashFrame(wx.Frame):
-    def __init__(self):
+    def __init__(self, app):
         wx.Frame.__init__(self, None, -1, 'Allmydata Tahoe')
+        self.app = app
 
         self.SetSizeHints(100, 100, 600, 800)
         self.SetIcon(amdicon.getIcon())
@@ -196,9 +215,16 @@ class SplashFrame(wx.Frame):
         self.Fit()
         self.Layout()
 
+        # plumb up event handler for abort
+        self.Connect(-1, -1, ABORT_EVENT_ID, self.wx_abort)
+
     def on_close(self, event):
         self.Show(False)
 
+    def wx_abort(self, event):
+        wx.MessageBox(event.failure.getTraceback(), 'Fatal Error in Node startup')
+        self.app.ExitMainLoop()
+
 class SplashPanel(wx.Panel):
     def __init__(self, parent, on_close):
         wx.Panel.__init__(self, parent, -1)
@@ -369,7 +395,7 @@ class MacGuiApp(wx.App):
 
     def OnInit(self):
         try:
-            self.frame = SplashFrame()
+            self.frame = SplashFrame(self)
             self.frame.Show(True)
             self.SetTopWindow(self.frame)
 
index ae1f9229c0486534ffa803cd248a9b7cde72449d..5ce189ec8698fb7b10cdfc9be8a7856726ace309 100644 (file)
@@ -172,11 +172,15 @@ class Node(service.MultiService):
             print "Node._startService failed, aborting"
             print failure
             #reactor.stop() # for unknown reasons, reactor.stop() isn't working.  [ ] TODO
-            self.log('calling os.abort()')
-            print "calling os.abort()"
-            os.abort()
+            self._abort_process(failure)
         d.addErrback(_die)
 
+    def _abort_process(self, failure):
+        self.log('calling os.abort()')
+        log('calling os.abort()')
+        print "calling os.abort()"
+        os.abort()
+
     def stopService(self):
         self.log("Node.stopService")
         d = self._tub_ready_observerlist.when_fired()