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:np_codegen/np_codegen.dart';
|
||||
import 'package:np_collection/np_collection.dart';
|
||||
import 'package:np_platform_lock/np_platform_lock.dart';
|
||||
|
||||
part 'database.g.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
|
||||
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 block(this);
|
||||
});
|
||||
|
@ -135,7 +135,7 @@ extension SqliteDbExtension on SqliteDb {
|
|||
///
|
||||
/// This function does not start a transaction, see [use] instead
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ extension SqliteDbExtension on SqliteDb {
|
|||
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
|
||||
// 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
|
||||
// access it in other isolates
|
||||
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 'file_saver.dart';
|
||||
export 'google_gps_map.dart';
|
||||
export 'lock.dart';
|
||||
export 'notification.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 'file_saver.dart';
|
||||
export 'google_gps_map.dart';
|
||||
export 'lock.dart';
|
||||
export 'notification.dart';
|
||||
export 'universal_storage.dart';
|
||||
|
|
|
@ -1004,6 +1004,13 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
np_platform_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "../np_platform_util"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
np_string:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1504,7 +1511,7 @@ packages:
|
|||
source: hosted
|
||||
version: "1.2.0"
|
||||
synchronized:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
|
||||
|
|
|
@ -127,7 +127,6 @@ dependencies:
|
|||
smooth_corner: ^1.1.0
|
||||
sqlite3: any
|
||||
sqlite3_flutter_libs: ^0.5.15
|
||||
synchronized: ^3.1.0
|
||||
to_string:
|
||||
git:
|
||||
url: https://gitlab.com/nkming2/dart-to-string
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:np_platform_lock/src/k.dart' as k;
|
||||
import 'package:np_platform_lock/src/raw_lock.dart';
|
||||
|
||||
class Lock {
|
||||
static Future<bool> tryLock(int lockId) async {
|
||||
return (await _channel.invokeMethod<bool>("tryLock", <String, dynamic>{
|
||||
"lockId": lockId,
|
||||
}))!;
|
||||
}
|
||||
class PlatformLock {
|
||||
static Future<T> synchronized<T>(int lockId, Future<T> Function() fn) =>
|
||||
RawLockInterface().synchronized(lockId, fn);
|
||||
|
||||
static Future<void> unlock(int lockId) =>
|
||||
_channel.invokeMethod("unlock", <String, dynamic>{
|
||||
"lockId": lockId,
|
||||
});
|
||||
|
||||
static const _channel = MethodChannel("${k.libId}/lock");
|
||||
static Future<void> forceUnlock(int lockId) =>
|
||||
RawLockInterface().forceUnlock(lockId);
|
||||
}
|
||||
|
|
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:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
np_platform_util:
|
||||
path: ../np_platform_util
|
||||
synchronized: ^3.1.0
|
||||
|
||||
dev_dependencies:
|
||||
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