From 8a567380df2680da2911fc9e279a2f2ab172b226 Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 5 Dec 2023 20:17:44 +0000 Subject: [PATCH] Lock storage io --- .../java/io/xpipe/app/storage/DataStorage.java | 15 +++++++++++++-- .../io/xpipe/app/storage/StandardStorage.java | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorage.java b/app/src/main/java/io/xpipe/app/storage/DataStorage.java index 68ad3ff0a..51ee8213b 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -20,6 +20,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -58,6 +59,8 @@ public abstract class DataStorage { @Getter private final List listeners = new CopyOnWriteArrayList<>(); + protected final ReentrantLock busyIo = new ReentrantLock(); + public DataStorage() { this.dir = AppPrefs.get().storageDirectory().getValue(); this.storeEntries = new ConcurrentHashMap<>(); @@ -146,8 +149,16 @@ public abstract class DataStorage { public abstract void load(); public void saveAsync() { - // TODO: Don't make this a daemon thread to guarantee proper saving - ThreadHelper.unstarted(this::save).start(); + // If we are already loading or saving, don't queue up another operation. + // This could otherwise lead to thread starvation with virtual threads + // Technically the load and save operations also return instantly if locked, but let's not even create new threads here + if (busyIo.isLocked()) { + return; + } + + ThreadHelper.runAsync(() -> { + save(); + }); } public abstract void save(); diff --git a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java index ad241aac3..ce7b3fc2f 100644 --- a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java @@ -110,7 +110,11 @@ public class StandardStorage extends DataStorage { } } - public synchronized void load() { + public void load() { + if (!busyIo.tryLock()) { + return; + } + this.gitStorageHandler.beforeStorageLoad(); var storesDir = getStoresDir(); @@ -294,14 +298,19 @@ public class StandardStorage extends DataStorage { deleteLeftovers(); loaded = true; + busyIo.unlock(); this.gitStorageHandler.afterStorageLoad(); } - public synchronized void save() { + public void save() { if (!loaded || disposed) { return; } + if (!busyIo.tryLock()) { + return; + } + this.gitStorageHandler.beforeStorageSave(); try { @@ -354,6 +363,7 @@ public class StandardStorage extends DataStorage { deleteLeftovers(); gitStorageHandler.afterStorageSave(); + busyIo.unlock(); } @Override