Skip to content

Commit 90f0fb8

Browse files
fix: prevent file I/O on audio thread, add safety guard in display mode lambda
- oboe_bridge.cpp: eagerly compute big-core mask in start() so the audio callback never calls computeBigCoreMask() (which does fopen). Defensive fallback in callback uses upper-half heuristic instead of file I/O if cache is somehow cold. - OsuGameAndroid.cs: wrap Schedule lambda in SelectHighestRefreshRate() with try-catch to prevent unhandled exceptions from stale display references. Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/df8487e1-1028-44d9-adc9-42e5e885e5a3 Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
1 parent dcbf502 commit 90f0fb8

2 files changed

Lines changed: 39 additions & 10 deletions

File tree

osu.Android/Native/oboe_bridge.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,17 @@ bool OboeBridge::start() {
171171

172172
active_.store(true);
173173
affinitySet_.store(false);
174+
175+
// Eagerly compute and cache the big-core mask BEFORE the audio callback runs.
176+
// This ensures computeBigCoreMask() (which does file I/O via fopen on sysfs)
177+
// never executes on the real-time audio thread where it could cause latency
178+
// spikes or priority inversion.
179+
int mask = cachedBigCoreMask.load(std::memory_order_relaxed);
180+
if (mask < 0) {
181+
mask = computeBigCoreMask();
182+
cachedBigCoreMask.store(mask, std::memory_order_relaxed);
183+
}
184+
174185
LOGI("Oboe stream started");
175186
return true;
176187
}
@@ -272,9 +283,20 @@ oboe::DataCallbackResult OboeBridge::onAudioReady(
272283
// Uses sysfs-based topology detection for accurate big-core identification
273284
// across all SoC vendors (Snapdragon, Exynos, Dimensity, Tensor).
274285
if (!affinitySet_.load(std::memory_order_relaxed)) {
286+
// cachedBigCoreMask is eagerly computed in start(), so this load
287+
// should always return >= 0. The < 0 branch is a defensive fallback
288+
// that avoids file I/O — it uses the generic upper-half heuristic
289+
// instead of calling computeBigCoreMask() on the audio thread.
275290
int bigMask = cachedBigCoreMask.load(std::memory_order_relaxed);
276291
if (bigMask < 0) {
277-
bigMask = computeBigCoreMask();
292+
// Defensive: sysfs was never read (should not happen).
293+
// Use upper-half heuristic instead of doing file I/O here.
294+
int num_cores = sysconf(_SC_NPROCESSORS_CONF);
295+
bigMask = 0;
296+
if (num_cores > 1) {
297+
for (int i = num_cores / 2; i < std::min(num_cores, 32); ++i)
298+
bigMask |= (1 << i);
299+
}
278300
cachedBigCoreMask.store(bigMask, std::memory_order_relaxed);
279301
}
280302

osu.Android/OsuGameAndroid.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -483,15 +483,22 @@ public void SelectHighestRefreshRate()
483483

484484
Schedule(() =>
485485
{
486-
AvailableDisplayRefreshRates.Clear();
487-
AvailableDisplayRefreshRates.Add(0); // 0 = "Auto (highest)"
488-
AvailableDisplayRefreshRates.AddRange(rates);
489-
490-
// If user hasn't selected a rate, auto-select highest.
491-
if (SelectedDisplayRefreshRate.Value == 0)
492-
applyDisplayMode(display, modes.OrderByDescending(m => m.RefreshRate).First());
493-
else
494-
applyRefreshRate(SelectedDisplayRefreshRate.Value);
486+
try
487+
{
488+
AvailableDisplayRefreshRates.Clear();
489+
AvailableDisplayRefreshRates.Add(0); // 0 = "Auto (highest)"
490+
AvailableDisplayRefreshRates.AddRange(rates);
491+
492+
// If user hasn't selected a rate, auto-select highest.
493+
if (SelectedDisplayRefreshRate.Value == 0)
494+
applyDisplayMode(display, modes.OrderByDescending(m => m.RefreshRate).First());
495+
else
496+
applyRefreshRate(SelectedDisplayRefreshRate.Value);
497+
}
498+
catch (Exception ex)
499+
{
500+
Debug.WriteLine($"[osu!] Failed to apply initial display mode: {ex.Message}");
501+
}
495502
});
496503

497504
Logger.Log($"[osu!] Display modes queried: {string.Join(", ", rates.Select(r => $"{r}Hz"))} (DeX={gameActivity.IsDeX})", LoggingTarget.Performance);

0 commit comments

Comments
 (0)