Skip to content
Permalink
Browse files
Merge pull request #11103 from JosJuice/android-gamefilecache-not-null
Android: Allocate GameFileCache on GUI thread
  • Loading branch information
lioncash committed Sep 29, 2022
2 parents 4f5a6ee + d4709ce commit 865348c
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 78 deletions.
@@ -74,7 +74,7 @@ private void initResources()
});

DirectoryInitialization.start(this);
GameFileCacheManager.startLoad(this);
GameFileCacheManager.startLoad();
}

/**
@@ -233,7 +233,7 @@ public void saveSettings(SettingsActivityView view, Context context)
if (mLoadedRecursiveIsoPathsValue != BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.getBoolean(this))
{
// Refresh game library
GameFileCacheManager.startRescan(context);
GameFileCacheManager.startRescan();
}
}
else
@@ -109,11 +109,11 @@ public static String[] getAllGamePaths()

public static native String[] getAllGamePaths(String[] folderPaths, boolean recursiveScan);

public native int getSize();
public synchronized native int getSize();

public native GameFile[] getAllGames();
public synchronized native GameFile[] getAllGames();

public native GameFile addOrGet(String gamePath);
public synchronized native GameFile addOrGet(String gamePath);

/**
* Sets the list of games to cache.
@@ -123,17 +123,17 @@ public static String[] getAllGamePaths()
*
* @return true if the cache was modified
*/
public native boolean update(String[] gamePaths);
public synchronized native boolean update(String[] gamePaths);

/**
* For each game that already is in the cache, scans the folder that contains the game
* for additional metadata files (PNG/XML).
*
* @return true if the cache was modified
*/
public native boolean updateAdditionalMetadata();
public synchronized native boolean updateAdditionalMetadata();

public native boolean load();
public synchronized native boolean load();

public native boolean save();
public synchronized native boolean save();
}
@@ -2,8 +2,6 @@

package org.dolphinemu.dolphinemu.services;

import android.content.Context;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

@@ -23,27 +21,28 @@
*/
public final class GameFileCacheManager
{
private static GameFileCache gameFileCache = null;
private static final MutableLiveData<GameFile[]> gameFiles =
private static GameFileCache sGameFileCache = null;
private static final MutableLiveData<GameFile[]> sGameFiles =
new MutableLiveData<>(new GameFile[]{});
private static boolean runRescanAfterLoad = false;
private static boolean sFirstLoadDone = false;
private static boolean sRunRescanAfterLoad = false;

private static final ExecutorService executor = Executors.newFixedThreadPool(1);
private static final MutableLiveData<Boolean> loadInProgress = new MutableLiveData<>(false);
private static final MutableLiveData<Boolean> rescanInProgress = new MutableLiveData<>(false);
private static final ExecutorService sExecutor = Executors.newFixedThreadPool(1);
private static final MutableLiveData<Boolean> sLoadInProgress = new MutableLiveData<>(false);
private static final MutableLiveData<Boolean> sRescanInProgress = new MutableLiveData<>(false);

private GameFileCacheManager()
{
}

public static LiveData<GameFile[]> getGameFiles()
{
return gameFiles;
return sGameFiles;
}

public static List<GameFile> getGameFilesForPlatform(Platform platform)
{
GameFile[] allGames = gameFiles.getValue();
GameFile[] allGames = sGameFiles.getValue();
ArrayList<GameFile> platformGames = new ArrayList<>();
for (GameFile game : allGames)
{
@@ -57,7 +56,7 @@ public static List<GameFile> getGameFilesForPlatform(Platform platform)

public static GameFile getGameFileByGameId(String gameId)
{
GameFile[] allGames = gameFiles.getValue();
GameFile[] allGames = sGameFiles.getValue();
for (GameFile game : allGames)
{
if (game.getGameId().equals(gameId))
@@ -72,7 +71,7 @@ public static GameFile findSecondDisc(GameFile game)
{
GameFile matchWithoutRevision = null;

GameFile[] allGames = gameFiles.getValue();
GameFile[] allGames = sGameFiles.getValue();
for (GameFile otherGame : allGames)
{
if (game.getGameId().equals(otherGame.getGameId()) &&
@@ -102,34 +101,36 @@ public static String[] findSecondDiscAndGetPaths(GameFile gameFile)
*/
public static LiveData<Boolean> isLoading()
{
return loadInProgress;
return sLoadInProgress;
}

/**
* Returns true if in the process of rescanning.
*/
public static LiveData<Boolean> isRescanning()
{
return rescanInProgress;
return sRescanInProgress;
}

public static boolean isLoadingOrRescanning()
{
return loadInProgress.getValue() || rescanInProgress.getValue();
return sLoadInProgress.getValue() || sRescanInProgress.getValue();
}

/**
* Asynchronously loads the game file cache from disk, without checking
* if the games are still present in the user's configured folders.
* If this has already been called, calling it again has no effect.
*/
public static void startLoad(Context context)
public static void startLoad()
{
if (!loadInProgress.getValue())
createGameFileCacheIfNeeded();

if (!sLoadInProgress.getValue())
{
loadInProgress.setValue(true);
sLoadInProgress.setValue(true);
new AfterDirectoryInitializationRunner().runWithoutLifecycle(
() -> executor.execute(GameFileCacheManager::load));
() -> sExecutor.execute(GameFileCacheManager::load));
}
}

@@ -139,22 +140,24 @@ public static void startLoad(Context context)
* If loading the game file cache hasn't started or hasn't finished,
* the execution of this will be postponed until it finishes.
*/
public static void startRescan(Context context)
public static void startRescan()
{
if (!rescanInProgress.getValue())
createGameFileCacheIfNeeded();

if (!sRescanInProgress.getValue())
{
rescanInProgress.setValue(true);
sRescanInProgress.setValue(true);
new AfterDirectoryInitializationRunner().runWithoutLifecycle(
() -> executor.execute(GameFileCacheManager::rescan));
() -> sExecutor.execute(GameFileCacheManager::rescan));
}
}

public static GameFile addOrGet(String gamePath)
{
// Common case: The game is in the cache, so just grab it from there.
// (Actually, addOrGet already checks for this case, but we want to avoid calling it if possible
// because onHandleIntent may hold a lock on gameFileCache for extended periods of time.)
GameFile[] allGames = gameFiles.getValue();
// because the executor thread may hold a lock on sGameFileCache for extended periods of time.)
GameFile[] allGames = sGameFiles.getValue();
for (GameFile game : allGames)
{
if (game.getPath().equals(gamePath))
@@ -165,10 +168,8 @@ public static GameFile addOrGet(String gamePath)

// Unusual case: The game wasn't found in the cache.
// Scan the game and add it to the cache so that we can return it.
synchronized (gameFileCache)
{
return gameFileCache.addOrGet(gamePath);
}
createGameFileCacheIfNeeded();
return sGameFileCache.addOrGet(gamePath);
}

/**
@@ -178,30 +179,26 @@ public static GameFile addOrGet(String gamePath)
*/
private static void load()
{
if (gameFileCache == null)
if (!sFirstLoadDone)
{
GameFileCache temp = new GameFileCache();
synchronized (temp)
sFirstLoadDone = true;
sGameFileCache.load();
if (sGameFileCache.getSize() != 0)
{
gameFileCache = temp;
gameFileCache.load();
if (gameFileCache.getSize() != 0)
{
updateGameFileArray();
}
updateGameFileArray();
}
}

if (runRescanAfterLoad)
if (sRunRescanAfterLoad)
{
rescanInProgress.postValue(true);
sRescanInProgress.postValue(true);
}

loadInProgress.postValue(false);
sLoadInProgress.postValue(false);

if (runRescanAfterLoad)
if (sRunRescanAfterLoad)
{
runRescanAfterLoad = false;
sRunRescanAfterLoad = false;
rescan();
}
}
@@ -214,43 +211,51 @@ private static void load()
*/
private static void rescan()
{
if (gameFileCache == null)
if (!sFirstLoadDone)
{
runRescanAfterLoad = true;
sRunRescanAfterLoad = true;
}
else
{
String[] gamePaths = GameFileCache.getAllGamePaths();

boolean changed;
synchronized (gameFileCache)
{
changed = gameFileCache.update(gamePaths);
}
boolean changed = sGameFileCache.update(gamePaths);
if (changed)
{
updateGameFileArray();
}

boolean additionalMetadataChanged = gameFileCache.updateAdditionalMetadata();
boolean additionalMetadataChanged = sGameFileCache.updateAdditionalMetadata();
if (additionalMetadataChanged)
{
updateGameFileArray();
}

if (changed || additionalMetadataChanged)
{
gameFileCache.save();
sGameFileCache.save();
}
}

rescanInProgress.postValue(false);
sRescanInProgress.postValue(false);
}

private static void updateGameFileArray()
{
GameFile[] gameFilesTemp = gameFileCache.getAllGames();
GameFile[] gameFilesTemp = sGameFileCache.getAllGames();
Arrays.sort(gameFilesTemp, (lhs, rhs) -> lhs.getTitle().compareToIgnoreCase(rhs.getTitle()));
gameFiles.postValue(gameFilesTemp);
sGameFiles.postValue(gameFilesTemp);
}

private static void createGameFileCacheIfNeeded()
{
// Creating the GameFileCache in the static initializer may be unsafe, because GameFileCache
// relies on native code, and the native library isn't loaded right when the app starts.
// We create it here instead.

if (sGameFileCache == null)
{
sGameFileCache = new GameFileCache();
}
}
}
@@ -301,7 +301,7 @@ public boolean onOptionsItemSelected(MenuItem item)
public void onRefresh()
{
setRefreshing(true);
GameFileCacheManager.startRescan(this);
GameFileCacheManager.startRescan();
}

/**
@@ -368,7 +368,7 @@ public void onTabSelected(@NonNull TabLayout.Tab tab)
mViewPager.setCurrentItem(IntSetting.MAIN_LAST_PLATFORM_TAB.getIntGlobal());

showGames();
GameFileCacheManager.startLoad(this);
GameFileCacheManager.startLoad();
}

@Override
@@ -96,7 +96,7 @@ public boolean handleOptionSelection(int itemId, ComponentActivity activity)

case R.id.menu_refresh:
mView.setRefreshing(true);
GameFileCacheManager.startRescan(activity);
GameFileCacheManager.startRescan();
return true;

case R.id.button_add_directory:
@@ -146,7 +146,7 @@ public void onResume()

if (sShouldRescanLibrary)
{
GameFileCacheManager.startRescan(mActivity);
GameFileCacheManager.startRescan();
}

sShouldRescanLibrary = true;
@@ -79,7 +79,7 @@ protected void onResume()
if (DirectoryInitialization.shouldStart(this))
{
DirectoryInitialization.start(this);
GameFileCacheManager.startLoad(this);
GameFileCacheManager.startLoad();
}

mPresenter.onResume();
@@ -292,7 +292,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
}

DirectoryInitialization.start(this);
GameFileCacheManager.startLoad(this);
GameFileCacheManager.startLoad();
}
}

@@ -303,7 +303,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
public void onRefresh()
{
setRefreshing(true);
GameFileCacheManager.startRescan(this);
GameFileCacheManager.startRescan();
}

private void buildRowsAdapter()
@@ -313,7 +313,7 @@ private void buildRowsAdapter()

if (!DirectoryInitialization.isWaitingForWriteAccess(this))
{
GameFileCacheManager.startLoad(this);
GameFileCacheManager.startLoad();
}

for (Platform platform : Platform.values())
@@ -13,8 +13,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
add_definitions(-D_CRT_NONSTDC_NO_WARNINGS)
add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
# The replacement for the old atomic shared_ptr functions was added in C++20, so we can't use it yet
add_definitions(-D_SILENCE_CXX20_OLD_SHARED_PTR_ATOMIC_SUPPORT_DEPRECATION_WARNING)
endif()

if (NOT MSVC)

0 comments on commit 865348c

Please sign in to comment.