Matthias Nott
10 days ago 6cbbea9b96db551e5c0ac26f0ace3d4c3d82a276
lib/services/message_store.dart
....@@ -15,6 +15,8 @@
1515 static Directory? _baseDir;
1616 static Timer? _debounceTimer;
1717 static final Map<String, List<Message>> _pendingSaves = {};
18
+ // Per-session lock to prevent concurrent read/write on the same file
19
+ static final Map<String, Completer<void>> _locks = {};
1820
1921 static const _backupChannel =
2022 MethodChannel('com.mnsoft.pailot/backup');
....@@ -60,10 +62,25 @@
6062
6163 /// Write directly to disk, bypassing debounce. For critical saves.
6264 static Future<void> writeDirect(String sessionId, List<Message> messages) async {
63
- // Cancel ALL pending debounce to prevent race with frozen iOS timers
6465 _debounceTimer?.cancel();
6566 _pendingSaves.remove(sessionId);
66
- await _writeSession(sessionId, messages);
67
+ await _withLock(sessionId, () => _writeSession(sessionId, messages));
68
+ }
69
+
70
+ /// Acquire a per-session lock, run the operation, release.
71
+ static Future<T> _withLock<T>(String sessionId, Future<T> Function() fn) async {
72
+ // Wait for any existing operation on this session to finish
73
+ while (_locks.containsKey(sessionId)) {
74
+ await _locks[sessionId]!.future;
75
+ }
76
+ final completer = Completer<void>();
77
+ _locks[sessionId] = completer;
78
+ try {
79
+ return await fn();
80
+ } finally {
81
+ _locks.remove(sessionId);
82
+ completer.complete();
83
+ }
6784 }
6885
6986 /// Immediately flush all pending saves.
....@@ -77,7 +94,7 @@
7794 _pendingSaves.clear();
7895
7996 for (final entry in entries.entries) {
80
- await _writeSession(entry.key, entry.value);
97
+ await _withLock(entry.key, () => _writeSession(entry.key, entry.value));
8198 }
8299 }
83100
....@@ -128,6 +145,10 @@
128145
129146 /// Load all messages for a session (no pagination).
130147 static Future<List<Message>> loadAll(String sessionId) async {
148
+ return _withLock(sessionId, () => _loadAllImpl(sessionId));
149
+ }
150
+
151
+ static Future<List<Message>> _loadAllImpl(String sessionId) async {
131152 try {
132153 final dir = await _getBaseDir();
133154 final file = File('${dir.path}/${_fileForSession(sessionId)}');