diff --git a/lib/platform/image_cache_io.dart b/lib/platform/image_cache_io.dart index 5317c70..d4aee93 100644 --- a/lib/platform/image_cache_io.dart +++ b/lib/platform/image_cache_io.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:logging/logging.dart'; +import 'package:mutex/mutex.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; @@ -33,6 +34,7 @@ class ImageCaches { static const version = 1; int _size = 0; int get size => _size; + late Mutex _mutex; ImageCaches(); Future _desktopFilePath() async { final String? exe = await platformPath.getCurrentExe(); @@ -166,6 +168,7 @@ class ImageCaches { await _createDir(); if (!(await _checkDatabase())) await _createTable(); await _updateSize(); + _mutex = Mutex(); _inited = true; } @@ -201,39 +204,42 @@ class ImageCaches { Future putCache(String uri, Uint8List data, Map> headers, String? realUri) async { if (!_inited) return; - final u = Uri.parse(uri); - final dir = await cacheDir; - String p = path.join(dir.path, u.host.isEmpty ? "nohost" : u.host, - u.path.substring(1) + (u.hasQuery ? "?${u.query}" : "")); - final d = _fs.directory(path.dirname(p)); - if (isWindows) { - if (path.isAbsolute(p)) { - p = p.substring(0, 2) + - p.substring(2).replaceAll(RegExp("[:\\*\\?\"\\<\\>\\|]"), '_'); - } else { - p = p.replaceAll(RegExp("[:\\*\\?\"\\<\\>\\|]"), '_'); + await _mutex.protect(() async { + final u = Uri.parse(uri); + final dir = await cacheDir; + String p = path.join(dir.path, u.host.isEmpty ? "nohost" : u.host, + u.path.substring(1) + (u.hasQuery ? "?${u.query}" : "")); + final d = _fs.directory(path.dirname(p)); + if (isWindows) { + if (path.isAbsolute(p)) { + p = p.substring(0, 2) + + p.substring(2).replaceAll(RegExp("[:\\*\\?\"\\<\\>\\|]"), '_'); + } else { + p = p.replaceAll(RegExp("[:\\*\\?\"\\<\\>\\|]"), '_'); + } } - } - if (!(await d.exists())) { - await d.create(recursive: true); - } - final f = _fs.file(p); - await f.writeAsBytes(data.toList()); - final lastUsed = DateTime.now().millisecondsSinceEpoch; - final header = jsonEncode(headers); - if (_exeDir != null) { - p = path.relative(p, from: _exeDir!); - } - final exes = await _db!.query("images", where: 'url = ?', whereArgs: [uri]); - await _db!.rawInsert( - "INSERT OR REPLACE INTO images VALUES (?, ?, ?, ?, ?, ?);", - [uri, p, lastUsed, header, realUri, data.length]); - if (exes.isEmpty) { - _size += data.length; - } else { - final originalSize = (exes[0]["size"] as int?) ?? 0; - _size += (data.length - originalSize); - } + if (!(await d.exists())) { + await d.create(recursive: true); + } + final f = _fs.file(p); + await f.writeAsBytes(data.toList()); + final lastUsed = DateTime.now().millisecondsSinceEpoch; + final header = jsonEncode(headers); + if (_exeDir != null) { + p = path.relative(p, from: _exeDir!); + } + final exes = + await _db!.query("images", where: 'url = ?', whereArgs: [uri]); + await _db!.rawInsert( + "INSERT OR REPLACE INTO images VALUES (?, ?, ?, ?, ?, ?);", + [uri, p, lastUsed, header, realUri, data.length]); + if (exes.isEmpty) { + _size += data.length; + } else { + final originalSize = (exes[0]["size"] as int?) ?? 0; + _size += (data.length - originalSize); + } + }); } Future updateSize({bool clear = false}) async { diff --git a/lib/platform/image_cache_web.dart b/lib/platform/image_cache_web.dart index 15bb945..d028d74 100644 --- a/lib/platform/image_cache_web.dart +++ b/lib/platform/image_cache_web.dart @@ -2,6 +2,7 @@ import 'dart:js_interop'; import 'dart:js_interop_unsafe'; import 'dart:typed_data'; import 'package:logging/logging.dart'; +import 'package:mutex/mutex.dart'; import 'package:web/web.dart'; import 'web/indexed_db.dart'; @@ -14,6 +15,7 @@ class ImageCaches { int get size => _size; late IndexedDb _db; bool _inited = false; + late Mutex _mutex; Future _updateSize() async { int total = 0; await _db.openCursor("images", (cur) { @@ -51,6 +53,7 @@ class ImageCaches { }, 1); await _db.init(); await _updateSize(); + _mutex = Mutex(); _inited = true; } @@ -90,27 +93,29 @@ class ImageCaches { Future putCache(String uri, Uint8List data, Map> headers, String? realUri) async { if (!_inited) return; - final he = JSObject(); - for (final e in headers.entries) { - he.setProperty(e.key.toJS, e.value.map((e) => e.toJS).toList().toJS); - } - final opts = ResponseInit(status: 200, statusText: 'OK', headers: he); - final lastUsed = DateTime.now().millisecondsSinceEpoch; - final res = Response(data.toJS, opts); - await cache.put(uri.toJS, res).toDart; - final oObj = (await _db.get("images", uri.toJS)) as JSObject?; - final obj = JSObject(); - obj.setProperty("url".toJS, uri.toJS); - obj.setProperty("size".toJS, data.length.toJS); - obj.setProperty("last_used".toJS, lastUsed.toJS); - await _db.put("images", obj); - if (oObj == null) { - _size += data.length; - } else { - final originalSize = - (oObj!.getProperty("size".toJS) as JSNumber).toDartInt; - _size += (data.length - originalSize); - } + await _mutex.protect(() async { + final he = JSObject(); + for (final e in headers.entries) { + he.setProperty(e.key.toJS, e.value.map((e) => e.toJS).toList().toJS); + } + final opts = ResponseInit(status: 200, statusText: 'OK', headers: he); + final lastUsed = DateTime.now().millisecondsSinceEpoch; + final res = Response(data.toJS, opts); + await cache.put(uri.toJS, res).toDart; + final oObj = (await _db.get("images", uri.toJS)) as JSObject?; + final obj = JSObject(); + obj.setProperty("url".toJS, uri.toJS); + obj.setProperty("size".toJS, data.length.toJS); + obj.setProperty("last_used".toJS, lastUsed.toJS); + await _db.put("images", obj); + if (oObj == null) { + _size += data.length; + } else { + final originalSize = + (oObj!.getProperty("size".toJS) as JSNumber).toDartInt; + _size += (data.length - originalSize); + } + }); } Future updateSize({bool clear = false}) async { diff --git a/pubspec.lock b/pubspec.lock index f155caa..e75d757 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -558,6 +558,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + mutex: + dependency: "direct main" + description: + name: mutex + sha256: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2" + url: "https://pub.dev" + source: hosted + version: "3.1.0" package_config: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 03cc645..a5c92b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: json_annotation: ^4.9.0 keymap: ^0.0.92 logging: ^1.2.0 + mutex: ^3.1.0 package_info_plus: ^8.0.0 palette_generator: ^0.3.3+3 path: ^1.8.3