Fix potential login freeze: skip blocking GStreamer registry rescan
Gst.init() has no async variant. If the plugin registry cache is stale (e.g. right after a system update), it synchronously rescans every plugin, spawning gst-plugin-scanner per plugin and probing hardware for codecs. Since gnome-shell is single-threaded and is the compositor, that can freeze the whole screen for several seconds, same as the blocking UPower proxy lookup fixed earlier. Set GST_REGISTRY_UPDATE=no when a cache already exists so Gst.init() trusts it and returns immediately, then refresh the cache in a throwaway subprocess afterward so it doesn't go stale forever. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,67 @@
|
||||
import Gio from 'gi://Gio';
|
||||
import GLib from 'gi://GLib';
|
||||
|
||||
/**
|
||||
* True if a GStreamer plugin registry cache already exists for this user.
|
||||
* A plain local directory listing — fast, unlike the scan below.
|
||||
*/
|
||||
function hasGstRegistryCache() {
|
||||
const cacheDir = Gio.File.new_for_path(
|
||||
GLib.build_filenamev([GLib.get_user_cache_dir(), 'gstreamer-1.0']));
|
||||
try {
|
||||
const enumerator = cacheDir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
|
||||
const hasEntry = enumerator.next_file(null) !== null;
|
||||
enumerator.close(null);
|
||||
return hasEntry;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gst.init() has no async variant. If its plugin registry cache is stale
|
||||
* (e.g. right after a system update touched GStreamer packages), it
|
||||
* synchronously rescans every plugin — spawning gst-plugin-scanner per
|
||||
* plugin, and probing hardware for codec plugins — which can block
|
||||
* gnome-shell's single main thread (it *is* the compositor) for many
|
||||
* seconds, exactly the conditions right after login. That froze the entire
|
||||
* screen the same way the blocking UPower proxy lookup did.
|
||||
*
|
||||
* GST_REGISTRY_UPDATE=no makes Gst.init() trust the existing cache
|
||||
* unconditionally instead of validating it, so it returns immediately. Only
|
||||
* set when a cache already exists — an extension-lifetime-first-ever
|
||||
* GStreamer run on this machine still needs one real scan to have anything
|
||||
* to trust.
|
||||
*/
|
||||
function keepGstInitFast() {
|
||||
if (hasGstRegistryCache())
|
||||
GLib.setenv('GST_REGISTRY_UPDATE', 'no', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the on-disk registry cache in a throwaway subprocess, so a
|
||||
* stale cache (skipped above) doesn't stay stale forever and newly
|
||||
* installed plugins eventually become visible. Runs fully out-of-process:
|
||||
* even if a hardware probe inside it hangs, gnome-shell is unaffected.
|
||||
*/
|
||||
function refreshGstRegistryInBackground() {
|
||||
try {
|
||||
const launcher = new Gio.SubprocessLauncher(
|
||||
Gio.SubprocessFlags.STDOUT_SILENCE | Gio.SubprocessFlags.STDERR_SILENCE);
|
||||
launcher.setenv('GST_REGISTRY_UPDATE', 'yes', true);
|
||||
const proc = launcher.spawnv(['gst-inspect-1.0', '--exists', 'playbin']);
|
||||
proc.wait_check_async(null, (source, result) => {
|
||||
try {
|
||||
source.wait_check_finish(result);
|
||||
} catch (e) {
|
||||
// Best-effort refresh; a missing binary or nonzero exit just leaves the cache as-is.
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// Best-effort refresh; a missing gst-inspect-1.0 just leaves the cache as-is.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the GObject-Introspection bindings the live wallpaper feature needs
|
||||
* (Gst, GstApp for the appsink signals, Cogl for uploading decoded frames)
|
||||
@@ -13,6 +77,8 @@
|
||||
* checkGstreamerBaseAvailable() there instead.
|
||||
*/
|
||||
export async function loadGstreamerModules() {
|
||||
keepGstInitFast();
|
||||
|
||||
const [{default: Gst}, , {default: Cogl}] = await Promise.all([
|
||||
import('gi://Gst?version=1.0'),
|
||||
import('gi://GstApp?version=1.0'),
|
||||
@@ -22,6 +88,8 @@ export async function loadGstreamerModules() {
|
||||
if (!Gst.is_initialized())
|
||||
Gst.init(null);
|
||||
|
||||
refreshGstRegistryInBackground();
|
||||
|
||||
return {Gst, Cogl};
|
||||
}
|
||||
|
||||
@@ -33,6 +101,8 @@ export async function loadGstreamerModules() {
|
||||
* failure here is a genuine, actionable problem, unlike a Cogl probe.
|
||||
*/
|
||||
export async function checkGstreamerBaseAvailable() {
|
||||
keepGstInitFast();
|
||||
|
||||
const [{default: Gst}] = await Promise.all([
|
||||
import('gi://Gst?version=1.0'),
|
||||
import('gi://GstApp?version=1.0'),
|
||||
@@ -40,6 +110,8 @@ export async function checkGstreamerBaseAvailable() {
|
||||
|
||||
if (!Gst.is_initialized())
|
||||
Gst.init(null);
|
||||
|
||||
refreshGstRegistryInBackground();
|
||||
}
|
||||
|
||||
export const GSTREAMER_INSTALL_HINT =
|
||||
|
||||
Reference in New Issue
Block a user