mirror of
https://github.com/venera-app/venera.git
synced 2025-12-15 14:41:15 +00:00
Improve updating following and fix potential crash.
This commit is contained in:
@@ -759,6 +759,7 @@ class ComicSourceParser {
|
||||
LoadComicFunc? _parseLoadComicFunc() {
|
||||
return (id) async {
|
||||
try {
|
||||
print("Loading comic $id from source $_key. Stacktrace:\n${StackTrace.current}");
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.comic.loadInfo(${jsonEncode(id)})
|
||||
""");
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:venera/foundation/favorites.dart';
|
||||
import 'package:venera/foundation/log.dart';
|
||||
import 'package:venera/utils/channel.dart';
|
||||
|
||||
class ComicUpdateResult {
|
||||
final bool updated;
|
||||
@@ -62,6 +63,7 @@ Future<ComicUpdateResult> updateComic(
|
||||
return ComicUpdateResult(updated, null);
|
||||
} catch (e, s) {
|
||||
Log.error("Check Updates", e, s);
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
retries--;
|
||||
if (retries == 0) {
|
||||
return ComicUpdateResult(false, e.toString());
|
||||
@@ -114,9 +116,36 @@ void updateFolderBase(
|
||||
current = 0;
|
||||
stream.add(UpdateProgress(total, current, errors, updated));
|
||||
|
||||
var futures = <Future>[];
|
||||
var channel = Channel<FavoriteItemWithUpdateInfo>(10);
|
||||
|
||||
// Producer
|
||||
() async {
|
||||
var c = 0;
|
||||
for (var comic in comicsToUpdate) {
|
||||
var future = updateComic(comic, folder).then((result) {
|
||||
await channel.push(comic);
|
||||
c++;
|
||||
// Throttle
|
||||
if (c % 5 == 0) {
|
||||
var delay = c % 100 + 1;
|
||||
if (delay > 10) {
|
||||
delay = 10;
|
||||
}
|
||||
await Future.delayed(Duration(seconds: delay));
|
||||
}
|
||||
}
|
||||
channel.close();
|
||||
}();
|
||||
|
||||
// Consumers
|
||||
var updateFutures = <Future>[];
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var f = () async {
|
||||
while (true) {
|
||||
var comic = await channel.pop();
|
||||
if (comic == null) {
|
||||
break;
|
||||
}
|
||||
var result = await updateComic(comic, folder);
|
||||
current++;
|
||||
if (result.updated) {
|
||||
updated++;
|
||||
@@ -124,13 +153,13 @@ void updateFolderBase(
|
||||
if (result.errorMessage != null) {
|
||||
errors++;
|
||||
}
|
||||
stream.add(
|
||||
UpdateProgress(total, current, errors, updated, comic, result.errorMessage));
|
||||
});
|
||||
futures.add(future);
|
||||
stream.add(UpdateProgress(total, current, errors, updated, comic, result.errorMessage));
|
||||
}
|
||||
}();
|
||||
updateFutures.add(f);
|
||||
}
|
||||
|
||||
await Future.wait(futures);
|
||||
await Future.wait(updateFutures);
|
||||
|
||||
if (updated > 0) {
|
||||
LocalFavoritesManager().notifyChanges();
|
||||
|
||||
58
lib/utils/channel.dart
Normal file
58
lib/utils/channel.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
class Channel<T> {
|
||||
final Queue<T> _queue;
|
||||
|
||||
final int size;
|
||||
|
||||
Channel(this.size) : _queue = Queue<T>();
|
||||
|
||||
Completer? _releaseCompleter;
|
||||
|
||||
Completer? _pushCompleter;
|
||||
|
||||
var currentSize = 0;
|
||||
|
||||
var isClosed = false;
|
||||
|
||||
Future<void> push(T item) async {
|
||||
if (currentSize >= size) {
|
||||
_releaseCompleter ??= Completer();
|
||||
return _releaseCompleter!.future.then((_) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
_queue.addLast(item);
|
||||
currentSize++;
|
||||
});
|
||||
}
|
||||
_queue.addLast(item);
|
||||
currentSize++;
|
||||
_pushCompleter?.complete();
|
||||
_pushCompleter = null;
|
||||
}
|
||||
|
||||
Future<T?> pop() async {
|
||||
while (_queue.isEmpty) {
|
||||
if (isClosed) {
|
||||
return null;
|
||||
}
|
||||
_pushCompleter ??= Completer();
|
||||
await _pushCompleter!.future;
|
||||
}
|
||||
var item = _queue.removeFirst();
|
||||
currentSize--;
|
||||
if (_releaseCompleter != null && currentSize < size) {
|
||||
_releaseCompleter!.complete();
|
||||
_releaseCompleter = null;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
void close() {
|
||||
isClosed = true;
|
||||
_pushCompleter?.complete();
|
||||
_releaseCompleter?.complete();
|
||||
}
|
||||
}
|
||||
115
test/channel_test.dart
Normal file
115
test/channel_test.dart
Normal file
@@ -0,0 +1,115 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:venera/utils/channel.dart';
|
||||
|
||||
void main() {
|
||||
test("1-1-1", () async {
|
||||
var channel = Channel<int>(1);
|
||||
await channel.push(1);
|
||||
var item = await channel.pop();
|
||||
expect(item, 1);
|
||||
});
|
||||
|
||||
test("1-3-1", () async {
|
||||
var channel = Channel<int>(1);
|
||||
|
||||
// producer
|
||||
() async {
|
||||
await channel.push(1);
|
||||
}();
|
||||
() async {
|
||||
await channel.push(2);
|
||||
}();
|
||||
() async {
|
||||
await channel.push(3);
|
||||
}();
|
||||
|
||||
// consumer
|
||||
var results = <int>[];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var item = await channel.pop();
|
||||
if (item != null) {
|
||||
results.add(item);
|
||||
}
|
||||
}
|
||||
expect(results.length, 3);
|
||||
});
|
||||
|
||||
test("2-3-1", () async {
|
||||
var channel = Channel<int>(2);
|
||||
|
||||
// producer
|
||||
() async {
|
||||
await channel.push(1);
|
||||
}();
|
||||
() async {
|
||||
await channel.push(2);
|
||||
}();
|
||||
() async {
|
||||
await channel.push(3);
|
||||
}();
|
||||
|
||||
// consumer
|
||||
var results = <int>[];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var item = await channel.pop();
|
||||
if (item != null) {
|
||||
results.add(item);
|
||||
}
|
||||
}
|
||||
expect(results.length, 3);
|
||||
});
|
||||
|
||||
test("1-1-3", () async {
|
||||
var channel = Channel<int>(1);
|
||||
|
||||
// producer
|
||||
() async {
|
||||
print("push 1");
|
||||
await channel.push(1);
|
||||
print("push 2");
|
||||
await channel.push(2);
|
||||
print("push 3");
|
||||
await channel.push(3);
|
||||
print("push done");
|
||||
channel.close();
|
||||
}();
|
||||
|
||||
// consumer
|
||||
var consumers = <Future>[];
|
||||
var results = <int>[];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
consumers.add(() async {
|
||||
while (true) {
|
||||
var item = await channel.pop();
|
||||
if (item == null) {
|
||||
break;
|
||||
}
|
||||
print("pop $item");
|
||||
results.add(item);
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
await Future.wait(consumers);
|
||||
expect(results.length, 3);
|
||||
});
|
||||
|
||||
test("close", () async {
|
||||
var channel = Channel<int>(2);
|
||||
|
||||
// producer
|
||||
() async {
|
||||
await channel.push(1);
|
||||
await channel.push(2);
|
||||
await channel.push(3);
|
||||
channel.close();
|
||||
}();
|
||||
|
||||
// consumer
|
||||
await channel.pop();
|
||||
await channel.pop();
|
||||
await channel.pop();
|
||||
var item4 = await channel.pop();
|
||||
expect(item4, null);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user