mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 08:46:18 +01:00
Refactor: move the higher level platform lock code to its own package
This commit is contained in:
parent
c496e2d1e2
commit
cd525f5168
20 changed files with 236 additions and 62 deletions
|
@ -17,6 +17,7 @@ import 'package:nc_photos/object_extension.dart';
|
||||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_collection/np_collection.dart';
|
import 'package:np_collection/np_collection.dart';
|
||||||
|
import 'package:np_platform_lock/np_platform_lock.dart';
|
||||||
|
|
||||||
part 'database.g.dart';
|
part 'database.g.dart';
|
||||||
part 'database/nc_album_extension.dart';
|
part 'database/nc_album_extension.dart';
|
||||||
|
|
|
@ -122,7 +122,7 @@ extension SqliteDbExtension on SqliteDb {
|
||||||
///
|
///
|
||||||
/// Do NOT call this when using [isolate], call [useInIsolate] instead
|
/// Do NOT call this when using [isolate], call [useInIsolate] instead
|
||||||
Future<T> use<T>(Future<T> Function(SqliteDb db) block) async {
|
Future<T> use<T>(Future<T> Function(SqliteDb db) block) async {
|
||||||
return await platform.Lock.synchronized(k.appDbLockId, () async {
|
return await PlatformLock.synchronized(k.appDbLockId, () async {
|
||||||
return await transaction(() async {
|
return await transaction(() async {
|
||||||
return await block(this);
|
return await block(this);
|
||||||
});
|
});
|
||||||
|
@ -135,7 +135,7 @@ extension SqliteDbExtension on SqliteDb {
|
||||||
///
|
///
|
||||||
/// This function does not start a transaction, see [use] instead
|
/// This function does not start a transaction, see [use] instead
|
||||||
Future<T> useNoTransaction<T>(Future<T> Function(SqliteDb db) block) async {
|
Future<T> useNoTransaction<T>(Future<T> Function(SqliteDb db) block) async {
|
||||||
return await platform.Lock.synchronized(k.appDbLockId, () async {
|
return await PlatformLock.synchronized(k.appDbLockId, () async {
|
||||||
return await block(this);
|
return await block(this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ extension SqliteDbExtension on SqliteDb {
|
||||||
Future<U> isolate<T, U>(T args, ComputeWithDbCallback<T, U> callback) async {
|
Future<U> isolate<T, U>(T args, ComputeWithDbCallback<T, U> callback) async {
|
||||||
// we need to acquire the lock here as method channel is not supported in
|
// we need to acquire the lock here as method channel is not supported in
|
||||||
// background isolates
|
// background isolates
|
||||||
return await platform.Lock.synchronized(k.appDbLockId, () async {
|
return await PlatformLock.synchronized(k.appDbLockId, () async {
|
||||||
// in unit tests we use an in-memory db, which mean there's no way to
|
// in unit tests we use an in-memory db, which mean there's no way to
|
||||||
// access it in other isolates
|
// access it in other isolates
|
||||||
if (platform_k.isUnitTest) {
|
if (platform_k.isUnitTest) {
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
|
||||||
import 'package:nc_photos/web/lock.dart' as web;
|
|
||||||
import 'package:np_platform_lock/np_platform_lock.dart' as plugin;
|
|
||||||
|
|
||||||
class Lock {
|
|
||||||
static Future<T> synchronized<T>(int lockId, Future<T> Function() fn) async {
|
|
||||||
if (platform_k.isAndroid) {
|
|
||||||
return _synchronizedAndroid(lockId, fn);
|
|
||||||
} else if (platform_k.isDesktop) {
|
|
||||||
return _synchronizedDesktop(lockId, fn);
|
|
||||||
} else {
|
|
||||||
throw UnimplementedError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<T> _synchronizedAndroid<T>(
|
|
||||||
int lockId, Future<T> Function() fn) async {
|
|
||||||
while (!await plugin.Lock.tryLock(lockId)) {
|
|
||||||
await Future.delayed(const Duration(milliseconds: 50));
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return await fn();
|
|
||||||
} finally {
|
|
||||||
await plugin.Lock.unlock(lockId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is mainly used to run test cases
|
|
||||||
static Future<T> _synchronizedDesktop<T>(
|
|
||||||
int lockId, Future<T> Function() fn) =>
|
|
||||||
web.Lock.synchronized(lockId, fn);
|
|
||||||
}
|
|
|
@ -2,6 +2,5 @@ export 'db_util.dart';
|
||||||
export 'download.dart';
|
export 'download.dart';
|
||||||
export 'file_saver.dart';
|
export 'file_saver.dart';
|
||||||
export 'google_gps_map.dart';
|
export 'google_gps_map.dart';
|
||||||
export 'lock.dart';
|
|
||||||
export 'notification.dart';
|
export 'notification.dart';
|
||||||
export 'universal_storage.dart';
|
export 'universal_storage.dart';
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import 'package:synchronized/synchronized.dart' as dart;
|
|
||||||
|
|
||||||
// Isolates are not supported on web
|
|
||||||
class Lock {
|
|
||||||
static Future<T> synchronized<T>(int lockId, Future<T> Function() fn) =>
|
|
||||||
(_locks[lockId] ??= dart.Lock(reentrant: true)).synchronized(fn);
|
|
||||||
|
|
||||||
static final _locks = <int, dart.Lock>{};
|
|
||||||
}
|
|
|
@ -2,6 +2,5 @@ export 'db_util.dart';
|
||||||
export 'download.dart';
|
export 'download.dart';
|
||||||
export 'file_saver.dart';
|
export 'file_saver.dart';
|
||||||
export 'google_gps_map.dart';
|
export 'google_gps_map.dart';
|
||||||
export 'lock.dart';
|
|
||||||
export 'notification.dart';
|
export 'notification.dart';
|
||||||
export 'universal_storage.dart';
|
export 'universal_storage.dart';
|
||||||
|
|
|
@ -1004,6 +1004,13 @@ packages:
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
np_platform_util:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "../np_platform_util"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
np_string:
|
np_string:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1504,7 +1511,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
synchronized:
|
synchronized:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: synchronized
|
name: synchronized
|
||||||
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
|
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
|
||||||
|
|
|
@ -127,7 +127,6 @@ dependencies:
|
||||||
smooth_corner: ^1.1.0
|
smooth_corner: ^1.1.0
|
||||||
sqlite3: any
|
sqlite3: any
|
||||||
sqlite3_flutter_libs: ^0.5.15
|
sqlite3_flutter_libs: ^0.5.15
|
||||||
synchronized: ^3.1.0
|
|
||||||
to_string:
|
to_string:
|
||||||
git:
|
git:
|
||||||
url: https://gitlab.com/nkming2/dart-to-string
|
url: https://gitlab.com/nkming2/dart-to-string
|
||||||
|
|
|
@ -1,17 +1,9 @@
|
||||||
import 'package:flutter/services.dart';
|
import 'package:np_platform_lock/src/raw_lock.dart';
|
||||||
import 'package:np_platform_lock/src/k.dart' as k;
|
|
||||||
|
|
||||||
class Lock {
|
class PlatformLock {
|
||||||
static Future<bool> tryLock(int lockId) async {
|
static Future<T> synchronized<T>(int lockId, Future<T> Function() fn) =>
|
||||||
return (await _channel.invokeMethod<bool>("tryLock", <String, dynamic>{
|
RawLockInterface().synchronized(lockId, fn);
|
||||||
"lockId": lockId,
|
|
||||||
}))!;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<void> unlock(int lockId) =>
|
static Future<void> forceUnlock(int lockId) =>
|
||||||
_channel.invokeMethod("unlock", <String, dynamic>{
|
RawLockInterface().forceUnlock(lockId);
|
||||||
"lockId": lockId,
|
|
||||||
});
|
|
||||||
|
|
||||||
static const _channel = MethodChannel("${k.libId}/lock");
|
|
||||||
}
|
}
|
||||||
|
|
17
np_platform_lock/lib/src/native/lock.dart
Normal file
17
np_platform_lock/lib/src/native/lock.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:np_platform_lock/src/k.dart' as k;
|
||||||
|
|
||||||
|
class Lock {
|
||||||
|
static Future<bool> tryLock(int lockId) async {
|
||||||
|
return (await _channel.invokeMethod<bool>("tryLock", <String, dynamic>{
|
||||||
|
"lockId": lockId,
|
||||||
|
}))!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> unlock(int lockId) =>
|
||||||
|
_channel.invokeMethod("unlock", <String, dynamic>{
|
||||||
|
"lockId": lockId,
|
||||||
|
});
|
||||||
|
|
||||||
|
static const _channel = MethodChannel("${k.libId}/lock");
|
||||||
|
}
|
41
np_platform_lock/lib/src/native/raw_lock.dart
Normal file
41
np_platform_lock/lib/src/native/raw_lock.dart
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import 'package:np_platform_lock/src/native/lock.dart';
|
||||||
|
import 'package:np_platform_lock/src/raw_lock.dart';
|
||||||
|
import 'package:np_platform_lock/src/web/raw_lock.dart' as web;
|
||||||
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
|
|
||||||
|
class RawLock implements RawLockInterface {
|
||||||
|
RawLock._();
|
||||||
|
|
||||||
|
factory RawLock() => _inst;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<T> synchronized<T>(int lockId, Future<T> Function() fn) async {
|
||||||
|
if (isUnitTest) {
|
||||||
|
return _synchronizedTest(lockId, fn);
|
||||||
|
} else {
|
||||||
|
return _synchronized(lockId, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> forceUnlock(int lockId) {
|
||||||
|
return Lock.unlock(lockId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T> _synchronized<T>(int lockId, Future<T> Function() fn) async {
|
||||||
|
while (!await Lock.tryLock(lockId)) {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 50));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await fn();
|
||||||
|
} finally {
|
||||||
|
await Lock.unlock(lockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is mainly used to run test cases
|
||||||
|
Future<T> _synchronizedTest<T>(int lockId, Future<T> Function() fn) =>
|
||||||
|
web.RawLock().synchronized(lockId, fn);
|
||||||
|
|
||||||
|
static final _inst = RawLock._();
|
||||||
|
}
|
17
np_platform_lock/lib/src/raw_lock.dart
Normal file
17
np_platform_lock/lib/src/raw_lock.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:np_platform_lock/src/native/raw_lock.dart'
|
||||||
|
if (dart.library.html) 'package:np_platform_lock/src/web/raw_lock.dart';
|
||||||
|
|
||||||
|
abstract class RawLockInterface {
|
||||||
|
factory RawLockInterface() => RawLock();
|
||||||
|
|
||||||
|
/// Safely run [fn] with an async lock
|
||||||
|
Future<T> synchronized<T>(int lockId, Future<T> Function() fn);
|
||||||
|
|
||||||
|
/// Forcefully unlock an async lock
|
||||||
|
///
|
||||||
|
/// This function is mostly for development use only, for example, to fix a
|
||||||
|
/// dangling lock after hot reload. This should not be used in production code
|
||||||
|
///
|
||||||
|
/// This method may not be supported by all implementations
|
||||||
|
Future<void> forceUnlock(int lockId);
|
||||||
|
}
|
19
np_platform_lock/lib/src/web/raw_lock.dart
Normal file
19
np_platform_lock/lib/src/web/raw_lock.dart
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import 'package:np_platform_lock/src/raw_lock.dart';
|
||||||
|
import 'package:synchronized/synchronized.dart';
|
||||||
|
|
||||||
|
class RawLock implements RawLockInterface {
|
||||||
|
RawLock._();
|
||||||
|
|
||||||
|
factory RawLock() => _inst;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<T> synchronized<T>(int lockId, Future<T> Function() fn) =>
|
||||||
|
(_locks[lockId] ??= Lock(reentrant: true)).synchronized(fn);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> forceUnlock(int lockId) => Future.value();
|
||||||
|
|
||||||
|
static final _inst = RawLock._();
|
||||||
|
|
||||||
|
final _locks = <int, Lock>{};
|
||||||
|
}
|
|
@ -11,6 +11,9 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
np_platform_util:
|
||||||
|
path: ../np_platform_util
|
||||||
|
synchronized: ^3.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
np_lints:
|
np_lints:
|
||||||
|
|
30
np_platform_util/.gitignore
vendored
Normal file
30
np_platform_util/.gitignore
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
10
np_platform_util/.metadata
Normal file
10
np_platform_util/.metadata
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: package
|
1
np_platform_util/analysis_options.yaml
Normal file
1
np_platform_util/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
include: package:np_lints/np.yaml
|
3
np_platform_util/lib/np_platform_util.dart
Normal file
3
np_platform_util/lib/np_platform_util.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
library np_platform_util;
|
||||||
|
|
||||||
|
export 'src/env.dart';
|
60
np_platform_util/lib/src/env.dart
Normal file
60
np_platform_util/lib/src/env.dart
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum NpPlatform {
|
||||||
|
android,
|
||||||
|
fuchsia,
|
||||||
|
iOs,
|
||||||
|
linux,
|
||||||
|
macOs,
|
||||||
|
web,
|
||||||
|
windows,
|
||||||
|
;
|
||||||
|
|
||||||
|
bool get isMobile => this == android || this == iOs;
|
||||||
|
bool get isDesktop =>
|
||||||
|
this == fuchsia || this == linux || this == macOs || this == windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current running platform
|
||||||
|
///
|
||||||
|
/// This function does not take the current context into account
|
||||||
|
NpPlatform getRawPlatform() {
|
||||||
|
if (kIsWeb) {
|
||||||
|
return NpPlatform.web;
|
||||||
|
} else {
|
||||||
|
return defaultTargetPlatform.toPlatform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current platform from [context]
|
||||||
|
NpPlatform getThemePlatform(BuildContext context) {
|
||||||
|
if (kIsWeb) {
|
||||||
|
return NpPlatform.web;
|
||||||
|
} else {
|
||||||
|
return Theme.of(context).platform.toPlatform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final isUnitTest = !kIsWeb && Platform.environment.containsKey("FLUTTER_TEST");
|
||||||
|
|
||||||
|
extension on TargetPlatform {
|
||||||
|
NpPlatform toPlatform() {
|
||||||
|
switch (this) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return NpPlatform.android;
|
||||||
|
case TargetPlatform.iOS:
|
||||||
|
return NpPlatform.iOs;
|
||||||
|
case TargetPlatform.linux:
|
||||||
|
return NpPlatform.linux;
|
||||||
|
case TargetPlatform.macOS:
|
||||||
|
return NpPlatform.macOs;
|
||||||
|
case TargetPlatform.windows:
|
||||||
|
return NpPlatform.windows;
|
||||||
|
case TargetPlatform.fuchsia:
|
||||||
|
return NpPlatform.fuchsia;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
np_platform_util/pubspec.yaml
Normal file
17
np_platform_util/pubspec.yaml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
name: np_platform_util
|
||||||
|
description: A new Flutter plugin project.
|
||||||
|
version: 0.0.1
|
||||||
|
homepage:
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.19.6 <3.0.0'
|
||||||
|
flutter: ">=3.3.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
np_lints:
|
||||||
|
path: ../np_lints
|
Loading…
Reference in a new issue