[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