Synchronize shell startup

The asynchronous nature of extension loading, session loading, and more,
makes the code racy as to what is initialized first, and hard to debug.
Additionally, since gjs is single-threaded, the only code we're running
in a thread anyway is readdir, which is going to be I/O bound, so the
code here is actually likely to be faster.

Drop this in favor of some good old fashioned synchronous loading.
This commit is contained in:
Jasper St. Pierre 2013-11-04 10:07:44 -05:00
parent 5f9e3edbe1
commit da4238ec68
7 changed files with 43 additions and 111 deletions

View File

@ -206,11 +206,11 @@ const Application = new Lang.Class({
_scanExtensions: function() { _scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder(); let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound)); finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
finder.scanExtensions(); finder.scanExtensions();
this._extensionsLoaded();
}, },
_extensionFound: function(signals, extension) { _extensionFound: function(finder, extension) {
let iter = this._model.append(); let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]); this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[extension.uuid] = iter; this._extensionIters[extension.uuid] = iter;

View File

@ -174,17 +174,9 @@ const ExtensionFinder = new Lang.Class({
this.emit('extension-found', extension); this.emit('extension-found', extension);
}, },
_extensionsLoaded: function() {
this.emit('extensions-loaded');
},
scanExtensions: function() { scanExtensions: function() {
let perUserDir = Gio.File.new_for_path(global.userdatadir); let perUserDir = Gio.File.new_for_path(global.userdatadir);
FileUtils.collectFromDatadirsAsync('extensions', FileUtils.collectFromDatadirs('extensions', true, Lang.bind(this, this._loadExtension, perUserDir));
{ processFile: Lang.bind(this, this._loadExtension),
loadedCallback: Lang.bind(this, this._extensionsLoaded),
includeUserDir: true,
data: perUserDir });
} }
}); });
Signals.addSignalMethods(ExtensionFinder.prototype); Signals.addSignalMethods(ExtensionFinder.prototype);

View File

@ -25,60 +25,27 @@ function listDirAsync(file, callback) {
}); });
} }
function _collectFromDirectoryAsync(dir, loadState) { function collectFromDatadirs(subdir, includeUserDir, processFile) {
function done() {
loadState.numLoading--;
if (loadState.loadedCallback &&
loadState.numLoading == 0)
loadState.loadedCallback(loadState.data);
}
dir.query_info_async('standard::type', Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_DEFAULT, null, function(object, res) {
try {
object.query_info_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
log(e.message);
done();
return;
}
listDirAsync(dir, Lang.bind(this, function(infos) {
for (let i = 0; i < infos.length; i++)
loadState.processFile(dir.get_child(infos[i].get_name()),
infos[i], loadState.data);
done();
}));
});
}
function collectFromDatadirsAsync(subdir, params) {
params = Params.parse(params, { includeUserDir: false,
processFile: null,
loadedCallback: null,
data: null });
let loadState = { data: params.data,
numLoading: 0,
loadedCallback: params.loadedCallback,
processFile: params.processFile };
if (params.processFile == null) {
if (params.loadedCallback)
params.loadedCallback(params.data);
return;
}
let dataDirs = GLib.get_system_data_dirs(); let dataDirs = GLib.get_system_data_dirs();
if (params.includeUserDir) if (includeUserDir)
dataDirs.unshift(GLib.get_user_data_dir()); dataDirs.unshift(GLib.get_user_data_dir());
loadState.numLoading = dataDirs.length;
for (let i = 0; i < dataDirs.length; i++) { for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]); let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
let dir = Gio.File.new_for_path(path); let dir = Gio.File.new_for_path(path);
_collectFromDirectoryAsync(dir, loadState); let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
processFile(fileEnum.get_child(info), info);
}
} }
} }

View File

@ -253,7 +253,7 @@ function onEnabledExtensionsChanged() {
newEnabledExtensions.filter(function(uuid) { newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1; return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) { }).forEach(function(uuid) {
enableExtension(uuid); enableExtension(uuid);
}); });
// Find and disable all the newly disabled extensions: UUIDs found in the // Find and disable all the newly disabled extensions: UUIDs found in the
@ -261,7 +261,7 @@ function onEnabledExtensionsChanged() {
enabledExtensions.filter(function(item) { enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1; return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) { }).forEach(function(uuid) {
disableExtension(uuid); disableExtension(uuid);
}); });
enabledExtensions = newEnabledExtensions; enabledExtensions = newEnabledExtensions;
@ -272,7 +272,7 @@ function _loadExtensions() {
enabledExtensions = getEnabledExtensions(); enabledExtensions = getEnabledExtensions();
let finder = new ExtensionUtils.ExtensionFinder(); let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) { finder.connect('extension-found', function(finder, extension) {
loadExtension(extension); loadExtension(extension);
}); });
finder.scanExtensions(); finder.scanExtensions();

View File

@ -112,11 +112,6 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME'); Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode(); sessionMode = new SessionMode.SessionMode();
sessionMode.connect('sessions-loaded', _sessionsLoaded);
sessionMode.init();
}
function _sessionsLoaded() {
sessionMode.connect('updated', _sessionUpdated); sessionMode.connect('updated', _sessionUpdated);
_initializePrefs(); _initializePrefs();
_initializeUI(); _initializeUI();

View File

@ -7,6 +7,7 @@ const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search; const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider'; const KEY_FILE_GROUP = 'Shell Search Provider';
@ -122,23 +123,7 @@ function loadRemoteSearchProviders(callback) {
return; return;
} }
let dataDirs = GLib.get_system_data_dirs(); FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
dataDirs.forEach(function(dataDir) {
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
let dir = Gio.File.new_for_path(path);
let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
loadRemoteSearchProvider(fileEnum.get_child(info));
}
});
let sortOrder = searchSettings.get_strv('sort-order'); let sortOrder = searchSettings.get_strv('sort-order');

View File

@ -102,19 +102,12 @@ const _modes = {
} }
}; };
function _getModes(modesLoadedCallback) { function _loadMode(file, info) {
FileUtils.collectFromDatadirsAsync('modes',
{ processFile: _loadMode,
loadedCallback: modesLoadedCallback,
data: _modes });
}
function _loadMode(file, info, loadedData) {
let name = info.get_name(); let name = info.get_name();
let suffix = name.indexOf('.json'); let suffix = name.indexOf('.json');
let modeName = suffix == -1 ? name : name.slice(name, suffix); let modeName = suffix == -1 ? name : name.slice(name, suffix);
if (loadedData.hasOwnProperty(modeName)) if (_modes.hasOwnProperty(modeName))
return; return;
let fileContent, success, tag, newMode; let fileContent, success, tag, newMode;
@ -125,18 +118,23 @@ function _loadMode(file, info, loadedData) {
return; return;
} }
loadedData[modeName] = {}; _modes[modeName] = {};
let propBlacklist = ['unlockDialog']; let propBlacklist = ['unlockDialog'];
for (let prop in loadedData[DEFAULT_MODE]) { for (let prop in loadedData[DEFAULT_MODE]) {
if (newMode[prop] !== undefined && if (newMode[prop] !== undefined &&
propBlacklist.indexOf(prop) == -1) propBlacklist.indexOf(prop) == -1)
loadedData[modeName][prop]= newMode[prop]; loadedData[modeName][prop] = newMode[prop];
} }
loadedData[modeName]['isPrimary'] = true; _modes[modeName]['isPrimary'] = true;
}
function _getModes() {
FileUtils.collectFromDatadirs('modes', false, _loadMode);
} }
function listModes() { function listModes() {
_getModes(function(modes) { let modes = _getModes();
modes.forEach(function() {
let names = Object.getOwnPropertyNames(modes); let names = Object.getOwnPropertyNames(modes);
for (let i = 0; i < names.length; i++) for (let i = 0; i < names.length; i++)
if (_modes[names[i]].isPrimary) if (_modes[names[i]].isPrimary)
@ -149,17 +147,12 @@ function listModes() {
const SessionMode = new Lang.Class({ const SessionMode = new Lang.Class({
Name: 'SessionMode', Name: 'SessionMode',
init: function() { _init: function() {
_getModes(Lang.bind(this, function(modes) { let isPrimary = (_modes[global.session_mode] &&
this._modes = modes; _modes[global.session_mode].isPrimary);
let primary = modes[global.session_mode] && let mode = isPrimary ? global.session_mode : 'user';
modes[global.session_mode].isPrimary; this._modeStack = [mode];
let mode = primary ? global.session_mode : 'user'; this._sync();
this._modeStack = [mode];
this._sync();
this.emit('sessions-loaded');
}));
}, },
pushMode: function(mode) { pushMode: function(mode) {
@ -186,13 +179,13 @@ const SessionMode = new Lang.Class({
}, },
_sync: function() { _sync: function() {
let params = this._modes[this.currentMode]; let params = _modes[this.currentMode];
let defaults; let defaults;
if (params.parentMode) if (params.parentMode)
defaults = Params.parse(this._modes[params.parentMode], defaults = Params.parse(_modes[params.parentMode],
this._modes[DEFAULT_MODE]); _modes[DEFAULT_MODE]);
else else
defaults = this._modes[DEFAULT_MODE]; defaults = _modes[DEFAULT_MODE];
params = Params.parse(params, defaults); params = Params.parse(params, defaults);
// A simplified version of Lang.copyProperties, handles // A simplified version of Lang.copyProperties, handles