From da4238ec681c7ca5f682867933e1f8fcbc623bf8 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Mon, 4 Nov 2013 10:07:44 -0500 Subject: [PATCH] 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. --- js/extensionPrefs/main.js | 4 +-- js/misc/extensionUtils.js | 10 +------ js/misc/fileUtils.js | 61 +++++++++------------------------------ js/ui/extensionSystem.js | 6 ++-- js/ui/main.js | 5 ---- js/ui/remoteSearch.js | 19 ++---------- js/ui/sessionMode.js | 49 ++++++++++++++----------------- 7 files changed, 43 insertions(+), 111 deletions(-) diff --git a/js/extensionPrefs/main.js b/js/extensionPrefs/main.js index 8e3355d70..a3f516e84 100644 --- a/js/extensionPrefs/main.js +++ b/js/extensionPrefs/main.js @@ -206,11 +206,11 @@ const Application = new Lang.Class({ _scanExtensions: function() { let finder = new ExtensionUtils.ExtensionFinder(); finder.connect('extension-found', Lang.bind(this, this._extensionFound)); - finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded)); finder.scanExtensions(); + this._extensionsLoaded(); }, - _extensionFound: function(signals, extension) { + _extensionFound: function(finder, extension) { let iter = this._model.append(); this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]); this._extensionIters[extension.uuid] = iter; diff --git a/js/misc/extensionUtils.js b/js/misc/extensionUtils.js index 7c6769a5a..afdc3cd3e 100644 --- a/js/misc/extensionUtils.js +++ b/js/misc/extensionUtils.js @@ -174,17 +174,9 @@ const ExtensionFinder = new Lang.Class({ this.emit('extension-found', extension); }, - _extensionsLoaded: function() { - this.emit('extensions-loaded'); - }, - scanExtensions: function() { let perUserDir = Gio.File.new_for_path(global.userdatadir); - FileUtils.collectFromDatadirsAsync('extensions', - { processFile: Lang.bind(this, this._loadExtension), - loadedCallback: Lang.bind(this, this._extensionsLoaded), - includeUserDir: true, - data: perUserDir }); + FileUtils.collectFromDatadirs('extensions', true, Lang.bind(this, this._loadExtension, perUserDir)); } }); Signals.addSignalMethods(ExtensionFinder.prototype); diff --git a/js/misc/fileUtils.js b/js/misc/fileUtils.js index 767c005fa..cec06d95f 100644 --- a/js/misc/fileUtils.js +++ b/js/misc/fileUtils.js @@ -25,60 +25,27 @@ function listDirAsync(file, callback) { }); } -function _collectFromDirectoryAsync(dir, loadState) { - 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; - } - +function collectFromDatadirs(subdir, includeUserDir, processFile) { let dataDirs = GLib.get_system_data_dirs(); - if (params.includeUserDir) + if (includeUserDir) dataDirs.unshift(GLib.get_user_data_dir()); - loadState.numLoading = dataDirs.length; for (let i = 0; i < dataDirs.length; i++) { let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]); 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); + } } } diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js index dde7b821d..40a48876e 100644 --- a/js/ui/extensionSystem.js +++ b/js/ui/extensionSystem.js @@ -253,7 +253,7 @@ function onEnabledExtensionsChanged() { newEnabledExtensions.filter(function(uuid) { return enabledExtensions.indexOf(uuid) == -1; }).forEach(function(uuid) { - enableExtension(uuid); + enableExtension(uuid); }); // Find and disable all the newly disabled extensions: UUIDs found in the @@ -261,7 +261,7 @@ function onEnabledExtensionsChanged() { enabledExtensions.filter(function(item) { return newEnabledExtensions.indexOf(item) == -1; }).forEach(function(uuid) { - disableExtension(uuid); + disableExtension(uuid); }); enabledExtensions = newEnabledExtensions; @@ -272,7 +272,7 @@ function _loadExtensions() { enabledExtensions = getEnabledExtensions(); let finder = new ExtensionUtils.ExtensionFinder(); - finder.connect('extension-found', function(signals, extension) { + finder.connect('extension-found', function(finder, extension) { loadExtension(extension); }); finder.scanExtensions(); diff --git a/js/ui/main.js b/js/ui/main.js index 6f961be7d..cf1a9a43b 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -112,11 +112,6 @@ function start() { Gio.DesktopAppInfo.set_desktop_env('GNOME'); sessionMode = new SessionMode.SessionMode(); - sessionMode.connect('sessions-loaded', _sessionsLoaded); - sessionMode.init(); -} - -function _sessionsLoaded() { sessionMode.connect('updated', _sessionUpdated); _initializePrefs(); _initializeUI(); diff --git a/js/ui/remoteSearch.js b/js/ui/remoteSearch.js index cd95069ea..4e7574c5a 100644 --- a/js/ui/remoteSearch.js +++ b/js/ui/remoteSearch.js @@ -7,6 +7,7 @@ const Lang = imports.lang; const St = imports.gi.St; const Shell = imports.gi.Shell; +const FileUtils = imports.misc.fileUtils; const Search = imports.ui.search; const KEY_FILE_GROUP = 'Shell Search Provider'; @@ -122,23 +123,7 @@ function loadRemoteSearchProviders(callback) { return; } - let dataDirs = GLib.get_system_data_dirs(); - 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)); - } - }); + FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider); let sortOrder = searchSettings.get_strv('sort-order'); diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js index eb04c6240..510adc5f4 100644 --- a/js/ui/sessionMode.js +++ b/js/ui/sessionMode.js @@ -102,19 +102,12 @@ const _modes = { } }; -function _getModes(modesLoadedCallback) { - FileUtils.collectFromDatadirsAsync('modes', - { processFile: _loadMode, - loadedCallback: modesLoadedCallback, - data: _modes }); -} - -function _loadMode(file, info, loadedData) { +function _loadMode(file, info) { let name = info.get_name(); let suffix = name.indexOf('.json'); let modeName = suffix == -1 ? name : name.slice(name, suffix); - if (loadedData.hasOwnProperty(modeName)) + if (_modes.hasOwnProperty(modeName)) return; let fileContent, success, tag, newMode; @@ -125,18 +118,23 @@ function _loadMode(file, info, loadedData) { return; } - loadedData[modeName] = {}; + _modes[modeName] = {}; let propBlacklist = ['unlockDialog']; for (let prop in loadedData[DEFAULT_MODE]) { if (newMode[prop] !== undefined && 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() { - _getModes(function(modes) { + let modes = _getModes(); + modes.forEach(function() { let names = Object.getOwnPropertyNames(modes); for (let i = 0; i < names.length; i++) if (_modes[names[i]].isPrimary) @@ -149,17 +147,12 @@ function listModes() { const SessionMode = new Lang.Class({ Name: 'SessionMode', - init: function() { - _getModes(Lang.bind(this, function(modes) { - this._modes = modes; - let primary = modes[global.session_mode] && - modes[global.session_mode].isPrimary; - let mode = primary ? global.session_mode : 'user'; - this._modeStack = [mode]; - this._sync(); - - this.emit('sessions-loaded'); - })); + _init: function() { + let isPrimary = (_modes[global.session_mode] && + _modes[global.session_mode].isPrimary); + let mode = isPrimary ? global.session_mode : 'user'; + this._modeStack = [mode]; + this._sync(); }, pushMode: function(mode) { @@ -186,13 +179,13 @@ const SessionMode = new Lang.Class({ }, _sync: function() { - let params = this._modes[this.currentMode]; + let params = _modes[this.currentMode]; let defaults; if (params.parentMode) - defaults = Params.parse(this._modes[params.parentMode], - this._modes[DEFAULT_MODE]); + defaults = Params.parse(_modes[params.parentMode], + _modes[DEFAULT_MODE]); else - defaults = this._modes[DEFAULT_MODE]; + defaults = _modes[DEFAULT_MODE]; params = Params.parse(params, defaults); // A simplified version of Lang.copyProperties, handles