Fix: synchronize method bodies instead of completely relying on synchronized colelctions

Should prevent ConcurrentModificationExceptions and runtime corruption
This commit is contained in:
artdeell 2023-01-30 22:58:19 +03:00
parent 3004503b84
commit 246bf5c1dc

View File

@ -1,19 +1,15 @@
package net.kdt.pojavlaunch.progresskeeper; package net.kdt.pojavlaunch.progresskeeper;
import android.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class ProgressKeeper { public class ProgressKeeper {
private static final ConcurrentHashMap<String, ConcurrentLinkedQueue<ProgressListener>> sProgressListeners = new ConcurrentHashMap<>(); private static final HashMap<String, List<ProgressListener>> sProgressListeners = new HashMap<>();
private static final ConcurrentHashMap<String, ProgressState> sProgressStates = new ConcurrentHashMap<>(); private static final HashMap<String, ProgressState> sProgressStates = new HashMap<>();
private static final List<TaskCountListener> sTaskCountListeners = Collections.synchronizedList(new ArrayList<>()); private static final List<TaskCountListener> sTaskCountListeners = new ArrayList<>();
public static void submitProgress(String progressRecord, int progress, int resid, Object... va) { public static synchronized void submitProgress(String progressRecord, int progress, int resid, Object... va) {
ProgressState progressState = sProgressStates.get(progressRecord); ProgressState progressState = sProgressStates.get(progressRecord);
boolean shouldCallStarted = progressState == null; boolean shouldCallStarted = progressState == null;
boolean shouldCallEnded = resid == -1 && progress == -1; boolean shouldCallEnded = resid == -1 && progress == -1;
@ -31,7 +27,7 @@ public class ProgressKeeper {
progressState.varArg = va; progressState.varArg = va;
} }
ConcurrentLinkedQueue<ProgressListener> progressListeners = sProgressListeners.get(progressRecord); List<ProgressListener> progressListeners = sProgressListeners.get(progressRecord);
if(progressListeners != null) if(progressListeners != null)
for(ProgressListener listener : progressListeners) { for(ProgressListener listener : progressListeners) {
if(shouldCallStarted) listener.onProgressStarted(); if(shouldCallStarted) listener.onProgressStarted();
@ -40,14 +36,14 @@ public class ProgressKeeper {
} }
} }
private static void updateTaskCount() { private static synchronized void updateTaskCount() {
int count = sProgressStates.size(); int count = sProgressStates.size();
for(TaskCountListener listener : sTaskCountListeners) { for(TaskCountListener listener : sTaskCountListeners) {
listener.onUpdateTaskCount(count); listener.onUpdateTaskCount(count);
} }
} }
public static void addListener(String progressRecord, ProgressListener listener) { public static synchronized void addListener(String progressRecord, ProgressListener listener) {
ProgressState state = sProgressStates.get(progressRecord); ProgressState state = sProgressStates.get(progressRecord);
if(state != null && (state.resid != -1 || state.progress != -1)) { if(state != null && (state.resid != -1 || state.progress != -1)) {
listener.onProgressStarted(); listener.onProgressStarted();
@ -55,32 +51,32 @@ public class ProgressKeeper {
}else{ }else{
listener.onProgressEnded(); listener.onProgressEnded();
} }
ConcurrentLinkedQueue<ProgressListener> listenerWeakReferenceList = sProgressListeners.get(progressRecord); List<ProgressListener> listenerWeakReferenceList = sProgressListeners.get(progressRecord);
if(listenerWeakReferenceList == null) sProgressListeners.put(progressRecord, (listenerWeakReferenceList = new ConcurrentLinkedQueue<>())); if(listenerWeakReferenceList == null) sProgressListeners.put(progressRecord, (listenerWeakReferenceList = new ArrayList<>()));
listenerWeakReferenceList.add(listener); listenerWeakReferenceList.add(listener);
} }
public static void removeListener(String progressRecord, ProgressListener listener) { public static synchronized void removeListener(String progressRecord, ProgressListener listener) {
ConcurrentLinkedQueue<ProgressListener> listenerWeakReferenceList = sProgressListeners.get(progressRecord); List<ProgressListener> listenerWeakReferenceList = sProgressListeners.get(progressRecord);
if(listenerWeakReferenceList != null) listenerWeakReferenceList.remove(listener); if(listenerWeakReferenceList != null) listenerWeakReferenceList.remove(listener);
} }
public static void addTaskCountListener(TaskCountListener listener) { public static synchronized void addTaskCountListener(TaskCountListener listener) {
listener.onUpdateTaskCount(sProgressStates.size()); listener.onUpdateTaskCount(sProgressStates.size());
if(!sTaskCountListeners.contains(listener)) sTaskCountListeners.add(listener); if(!sTaskCountListeners.contains(listener)) sTaskCountListeners.add(listener);
} }
public static void addTaskCountListener(TaskCountListener listener, boolean runUpdate) { public static synchronized void addTaskCountListener(TaskCountListener listener, boolean runUpdate) {
if(runUpdate) listener.onUpdateTaskCount(sProgressStates.size()); if(runUpdate) listener.onUpdateTaskCount(sProgressStates.size());
if(!sTaskCountListeners.contains(listener)) sTaskCountListeners.add(listener); if(!sTaskCountListeners.contains(listener)) sTaskCountListeners.add(listener);
} }
public static void removeTaskCountListener(TaskCountListener listener) { public static synchronized void removeTaskCountListener(TaskCountListener listener) {
sTaskCountListeners.remove(listener); sTaskCountListeners.remove(listener);
} }
/** /**
* Waits until all tasks are done and runs the runnable, or if there were no pending process remaining * Waits until all tasks are done and runs the runnable, or if there were no pending process remaining
* The runnable runs from the thread that updated the task count last, and it might be the UI thread, * The runnable runs from the thread that updated the task count last, and it might be the UI thread,
* so dont put long running processes in it * so don't put long running processes in it
* @param runnable the runnable to run when no tasks are remaining * @param runnable the runnable to run when no tasks are remaining
*/ */
public static void waitUntilDone(final Runnable runnable) { public static void waitUntilDone(final Runnable runnable) {
@ -101,7 +97,7 @@ public class ProgressKeeper {
addTaskCountListener(listener); addTaskCountListener(listener);
} }
public static int getTaskCount() { public static synchronized int getTaskCount() {
return sProgressStates.size(); return sProgressStates.size();
} }
} }