mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-05-15 12:42:14 -04:00
more refactor and dhtrecord multiple-open support
This commit is contained in:
parent
c4c7b264aa
commit
e262b0f777
19 changed files with 782 additions and 419 deletions
|
@ -3,4 +3,6 @@ library;
|
|||
|
||||
export 'src/async_tag_lock.dart';
|
||||
export 'src/async_value.dart';
|
||||
export 'src/single_async.dart';
|
||||
export 'src/serial_future.dart';
|
||||
export 'src/single_future.dart';
|
||||
export 'src/single_state_processor.dart';
|
||||
|
|
57
packages/async_tools/lib/src/serial_future.dart
Normal file
57
packages/async_tools/lib/src/serial_future.dart
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Process a single future at a time per tag queued serially
|
||||
//
|
||||
// The closure function is called to produce the future that is to be executed.
|
||||
// If a future with a particular tag is still executing, it is queued serially
|
||||
// and executed when the previous tagged future completes.
|
||||
// When a tagged serialFuture finishes executing, the onDone callback is called.
|
||||
// If an unhandled exception happens in the closure future, the onError callback
|
||||
// is called.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'async_tag_lock.dart';
|
||||
|
||||
AsyncTagLock<Object> _keys = AsyncTagLock();
|
||||
typedef SerialFutureQueueItem = Future<void> Function();
|
||||
Map<Object, Queue<SerialFutureQueueItem>> _queues = {};
|
||||
|
||||
SerialFutureQueueItem _makeSerialFutureQueueItem<T>(
|
||||
Future<T> Function() closure,
|
||||
void Function(T)? onDone,
|
||||
void Function(Object e, StackTrace? st)? onError) =>
|
||||
() async {
|
||||
try {
|
||||
final out = await closure();
|
||||
if (onDone != null) {
|
||||
onDone(out);
|
||||
}
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e, sp) {
|
||||
if (onError != null) {
|
||||
onError(e, sp);
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void serialFuture<T>(Object tag, Future<T> Function() closure,
|
||||
{void Function(T)? onDone,
|
||||
void Function(Object e, StackTrace? st)? onError}) {
|
||||
final queueItem = _makeSerialFutureQueueItem(closure, onDone, onError);
|
||||
if (!_keys.tryLock(tag)) {
|
||||
final queue = _queues[tag];
|
||||
queue!.add(queueItem);
|
||||
return;
|
||||
}
|
||||
final queue = _queues[tag] = Queue.from([queueItem]);
|
||||
unawaited(() async {
|
||||
do {
|
||||
final queueItem = queue.removeFirst();
|
||||
await queueItem();
|
||||
} while (queue.isNotEmpty);
|
||||
_queues.remove(tag);
|
||||
_keys.unlockTag(tag);
|
||||
}());
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'async_tag_lock.dart';
|
||||
|
||||
AsyncTagLock<Object> _keys = AsyncTagLock();
|
||||
|
||||
void singleFuture<T>(Object tag, Future<T> Function() closure,
|
||||
{void Function()? onBusy, void Function(T)? onDone}) {
|
||||
if (!_keys.tryLock(tag)) {
|
||||
if (onBusy != null) {
|
||||
onBusy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
unawaited(() async {
|
||||
try {
|
||||
final out = await closure();
|
||||
if (onDone != null) {
|
||||
onDone(out);
|
||||
}
|
||||
} finally {
|
||||
_keys.unlockTag(tag);
|
||||
}
|
||||
}());
|
||||
}
|
42
packages/async_tools/lib/src/single_future.dart
Normal file
42
packages/async_tools/lib/src/single_future.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'async_tag_lock.dart';
|
||||
|
||||
AsyncTagLock<Object> _keys = AsyncTagLock();
|
||||
|
||||
// Process a single future at a time per tag
|
||||
//
|
||||
// The closure function is called to produce the future that is to be executed.
|
||||
// If a future with a particular tag is still executing, the onBusy callback
|
||||
// is called.
|
||||
// When a tagged singleFuture finishes executing, the onDone callback is called.
|
||||
// If an unhandled exception happens in the closure future, the onError callback
|
||||
// is called.
|
||||
void singleFuture<T>(Object tag, Future<T> Function() closure,
|
||||
{void Function()? onBusy,
|
||||
void Function(T)? onDone,
|
||||
void Function(Object e, StackTrace? st)? onError}) {
|
||||
if (!_keys.tryLock(tag)) {
|
||||
if (onBusy != null) {
|
||||
onBusy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
unawaited(() async {
|
||||
try {
|
||||
final out = await closure();
|
||||
if (onDone != null) {
|
||||
onDone(out);
|
||||
}
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e, sp) {
|
||||
if (onError != null) {
|
||||
onError(e, sp);
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
} finally {
|
||||
_keys.unlockTag(tag);
|
||||
}
|
||||
}());
|
||||
}
|
46
packages/async_tools/lib/src/single_state_processor.dart
Normal file
46
packages/async_tools/lib/src/single_state_processor.dart
Normal file
|
@ -0,0 +1,46 @@
|
|||
import 'dart:async';
|
||||
|
||||
import '../async_tools.dart';
|
||||
|
||||
// Process a single state update at a time ensuring the most
|
||||
// recent state gets processed asynchronously, possibly skipping
|
||||
// states that happen while a previous state is still being processed.
|
||||
//
|
||||
// Eventually this will always process the most recent state passed to
|
||||
// updateState.
|
||||
//
|
||||
// This is useful for processing state changes asynchronously without waiting
|
||||
// from a synchronous execution context
|
||||
class SingleStateProcessor<State> {
|
||||
SingleStateProcessor();
|
||||
|
||||
void updateState(State newInputState,
|
||||
{required Future<void> Function(State) closure}) {
|
||||
// Use a singlefuture here to ensure we get dont lose any updates
|
||||
// If the input stream gives us an update while we are
|
||||
// still processing the last update, the most recent input state will
|
||||
// be saved and processed eventually.
|
||||
|
||||
singleFuture(this, () async {
|
||||
var newState = newInputState;
|
||||
var done = false;
|
||||
while (!done) {
|
||||
await closure(newState);
|
||||
|
||||
// See if there's another state change to process
|
||||
final next = _nextState;
|
||||
_nextState = null;
|
||||
if (next != null) {
|
||||
newState = next;
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}, onBusy: () {
|
||||
// Keep this state until we process again
|
||||
_nextState = newInputState;
|
||||
});
|
||||
}
|
||||
|
||||
State? _nextState;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue