[Code-review] [PATCH] Detect, log, and work around errors that occur during module preloading.

Michael Stone michael at laptop.org
Thu Apr 24 19:25:08 EDT 2008


NB: The current implementation of this module preloading hack is fantastically
stupid because it races with X (extra bad because X startup appears to vary
with locale), because it forces all python activities Rainbow launches to use
the same DISPLAY (probably prevents using Rainbow-launched activities in VNC or
over SSH X forwarding) and because it forces Rainbow to run lots of nasty GUI
and IPC code as uid 0!

Conclusion: in order to achieve our quality goals, we _must_ find a way to
write software that starts quickly and which does not rely on fragile hacks
like this one.
---
 rainbow/rainbow/inject.py  |   10 +++--
 rainbow/rainbow/service.py |   75 +++++++++++++++++++++++++++-----------------
 2 files changed, 52 insertions(+), 33 deletions(-)

diff --git a/rainbow/rainbow/inject.py b/rainbow/rainbow/inject.py
index b28963d..ad05626 100644
--- a/rainbow/rainbow/inject.py
+++ b/rainbow/rainbow/inject.py
@@ -186,7 +186,8 @@ def configure_home(spool, owner_uid, owner_gid, uid, gid, home):
     if not lexists(join(home, '.adobe')):
         os.symlink('instance', join(home, '.adobe'))
 
-def launch(log, home, uid, gid, argv, env, cwd, pset, safe_fds, strace_hint):
+def launch(log, home, uid, gid, argv, env, cwd, pset, safe_fds, strace_hint,
+           preloader_hint):
     env['USER'] = str(uid)
     env['HOME'] = home
     env['XAUTHORITY'] = join(home, '.Xauthority')
@@ -258,7 +259,7 @@ def launch(log, home, uid, gid, argv, env, cwd, pset, safe_fds, strace_hint):
 
     # This looks a bit hacky. Probably need a better way to know that this
     # activitiy needs to go through the fast path.
-    if argv[0] == 'sugar-activity':
+    if preloader_hint and argv[0] == 'sugar-activity':
 
         # hack: should be parameters to sugarlaunch.launch()
         sys.argv = argv
@@ -316,7 +317,8 @@ def check_home(uid, gid, home):
     assert ck.negative(W_OK, 0)
 
 def run(log, spool, env, argv, cwd, pset, safe_fds, strace_hint,
-        owner_uid, owner_gid, bundle_path, bundle_id, constant_uid):
+        owner_uid, owner_gid, bundle_path, bundle_id, constant_uid,
+        preloader_hint):
     # Note: exceptions are intended to bubble up to the caller and should
     # terminate execution.
     check_bundle_id(bundle_id)
@@ -337,4 +339,4 @@ def run(log, spool, env, argv, cwd, pset, safe_fds, strace_hint,
     check_home(uid, gid, home)
     check_bundle_path(uid, gid, bundle_path)
 
-    launch(log, home, uid, gid, argv, env, cwd, pset, safe_fds, strace_hint)
+    launch(log, home, uid, gid, argv, env, cwd, pset, safe_fds, strace_hint, preloader_hint)
diff --git a/rainbow/rainbow/service.py b/rainbow/rainbow/service.py
index 9ead257..d2b605f 100644
--- a/rainbow/rainbow/service.py
+++ b/rainbow/rainbow/service.py
@@ -1,7 +1,6 @@
 import os
-import sys
 from signal import SIGCHLD
-from optparse import OptionParser
+from time import time
 
 import gobject
 
@@ -29,6 +28,50 @@ class Rainbow(dbus.service.Object):
 
     def __init__(self, bus_or_name):
         dbus.service.Object.__init__(self, bus_or_name, '/')
+        self.preloader_hint = True
+        self.preload_common_modules()
+
+    def preload_common_modules(self):
+        # Import-time logic is quite sensitive to environment variables.
+        os.environ['XAUTHORITY'] = '/home/olpc/.Xauthority'
+        # Are we never going to want to launch python activities attached to other
+        # displays? <MS>
+        os.environ['DISPLAY'] = ':0'
+        os.environ['GTK2_RC_FILES'] = '/usr/share/sugar/data/sugar-xo.gtkrc'
+
+        imports = ('dbus.service',
+                   'dbus.decorators',
+                   'telepathy',
+                   'sugar.activity.activity',
+                   'sugar.activity.activityservice',
+                   'sugar.graphics.style',
+                   'sugar.graphics.window',
+                   'rainbow.sugaractivity',
+                  )
+
+        start_time = time()
+
+        for module in imports:
+            try:
+                __import__(module)
+            except:
+                print 'Exception when preloading module: %s' % module
+                self.preloader_hint = False
+                util.trace()
+                break
+
+        if self.preloader_hint:
+            try:
+                import gtk
+                display = gtk.gdk.display_get_default()
+                display.close() # This needs to happen after importing s.g.style
+            except:
+                print 'Exception when preloading module: gtk'
+                self.preloader_hint = False
+                util.trace()
+
+        print 'Module preloading took %f seconds.' % (time() - start_time)
+
 
     @dbus.service.method(INTERFACE_NAME,
                          in_signature='s')
@@ -79,7 +122,7 @@ class Rainbow(dbus.service.Object):
                     os.dup2(log_fd, 2)
                     ret = inject.run(log, SPOOL, env, argv, env['SUGAR_BUNDLE_PATH'], pset, (1, 2),
                             env.get('RAINBOW_STRACE_LOG'), 500, 500, bundle_path, bundle_id,
-                            env.get('RAINBOW_CONSTANT_UID'))
+                            env.get('RAINBOW_CONSTANT_UID'), self.preloader_hint)
                 except Exception, e:
                     util.trace()
                     error_cont(e)
@@ -92,30 +135,6 @@ class Rainbow(dbus.service.Object):
             util.trace()
             error_cont(e)
 
-def preload_common_modules():
-    # rainbow needs to be able to access the X connection.
-    os.environ['XAUTHORITY'] = '/home/olpc/.Xauthority'
-    os.environ['DISPLAY'] = ':0'
-
-    # This needs to be set before we ever import gtk, as some values get cached.
-    os.environ['GTK2_RC_FILES'] = '/usr/share/sugar/data/sugar-xo.gtkrc'
-
-    # Pre-load dbus
-    import dbus.service, dbus.decorators
-
-    # Pre-load telepathy
-    import telepathy
-
-    # Pre-load heavy sugar modules
-    from sugar.activity import activity, activityservice
-    from sugar.graphics import style, window
-    from rainbow import sugaractivity
-
-    # Pre-load pygtk
-    import gtk
-    display = gtk.gdk.display_get_default()
-    display.close() # This needs to happen after importing s.g.style
-
 def run(opts, args):
     """Start the Rainbow DBus service."""
     log("GC'ing spool %s", SPOOL)
@@ -132,8 +151,6 @@ def run(opts, args):
     name = dbus.service.BusName(Rainbow.SERVICE_NAME, bus)
     Rainbow(name)
 
-    preload_common_modules()
-
     print 'Service running mainloop.'
     mainloop = gobject.MainLoop()
     mainloop.run()
-- 
1.5.3.3



More information about the Code-review mailing list