add async and scheduler binding.

This commit is contained in:
Kevin Gu 2020-09-07 14:18:38 +08:00
Родитель b7c15be96a
Коммит ab477fe987
39 изменённых файлов: 3704 добавлений и 37 удалений

Просмотреть файл

@ -6,6 +6,7 @@ using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.scheduler2;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
@ -63,7 +64,7 @@ namespace UIWidgetsGallery.gallery {
this._options = new GalleryOptions(
theme: GalleryTheme.kLightGalleryTheme,
textScaleFactor: GalleryTextScaleValue.kAllGalleryTextScaleValues[0],
timeDilation: SchedulerBinding.instance.timeDilation,
timeDilation: scheduler_.timeDilation,
platform: Application.platform,
showPerformanceOverlay: this.widget.enablePerformanceOverlay
);
@ -82,9 +83,9 @@ namespace UIWidgetsGallery.gallery {
this._timeDilationTimer = null;
if (newOptions.timeDilation > 1.0f) {
this._timeDilationTimer = Window.instance.run(new TimeSpan(0, 0, 0, 0, 150),
() => { SchedulerBinding.instance.timeDilation = newOptions.timeDilation; });
() => { scheduler_.timeDilation = newOptions.timeDilation; });
} else {
SchedulerBinding.instance.timeDilation = newOptions.timeDilation;
scheduler_.timeDilation = newOptions.timeDilation;
}
}

Просмотреть файл

@ -68,6 +68,24 @@ namespace Unity.UIWidgets.async {
get { return this._data.Count; }
}
public bool isEmpty {
get { return count == 0; }
}
public bool isNotEmpty {
get { return count != 0; }
}
public T first {
get {
return peek();
}
}
public T removeFirst() {
return dequeue();
}
public override string ToString() {
string s = "";
for (int i = 0; i < this._data.Count; ++i) {

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0dd4bb15542f4ad9a7bef43d7defab5f
timeCreated: 1599458064

Просмотреть файл

@ -0,0 +1,360 @@
using System;
using System.Collections.Generic;
namespace Unity.UIWidgets.async2 {
public struct FutureOr {
public object value;
public Future future;
public bool isFuture => future != null;
public static FutureOr withValue(object value) {
return new FutureOr {value = value};
}
public static FutureOr withFuture(Future future) {
return new FutureOr {future = future};
}
public static readonly FutureOr nullValue = withValue(null);
public static readonly FutureOr trueValue = withValue(true);
public static readonly FutureOr falseValue = withValue(false);
}
public abstract class Future {
static readonly _Future _nullFuture = _Future.zoneValue(null, Zone.root);
static readonly _Future _falseFuture = _Future.zoneValue(false, Zone.root);
public static Future create(Func<FutureOr> computation) {
_Future result = new _Future();
Timer.run(() => {
try {
result._complete(computation());
}
catch (Exception e) {
async_._completeWithErrorCallback(result, e);
}
return null;
});
return result;
}
public static Future microtask(Func<FutureOr> computation) {
_Future result = new _Future();
async_.scheduleMicrotask(() => {
try {
result._complete(computation());
}
catch (Exception e) {
async_._completeWithErrorCallback(result, e);
}
return null;
});
return result;
}
public static Future sync(Func<FutureOr> computation) {
try {
var result = computation();
if (result.isFuture) {
return result.future;
}
else {
return _Future.value(result);
}
}
catch (Exception error) {
var future = new _Future();
AsyncError replacement = Zone.current.errorCallback(error);
if (replacement != null) {
future._asyncCompleteError(async_._nonNullError(replacement.InnerException));
}
else {
future._asyncCompleteError(error);
}
return future;
}
}
public static Future value(FutureOr value = default) {
return _Future.immediate(value);
}
public static Future error(Exception error) {
if (error == null)
throw new ArgumentNullException(nameof(error));
if (!ReferenceEquals(Zone.current, async_._rootZone)) {
AsyncError replacement = Zone.current.errorCallback(error);
if (replacement != null) {
error = async_._nonNullError(replacement.InnerException);
}
}
return _Future.immediateError(error);
}
public static Future delayed(TimeSpan duration, Func<FutureOr> computation = null) {
_Future result = new _Future();
Timer.create(duration, () => {
if (computation == null) {
result._complete(FutureOr.nullValue);
}
else {
try {
result._complete(computation());
}
catch (Exception e) {
async_._completeWithErrorCallback(result, e);
}
}
return null;
});
return result;
}
public static Future wait<T>(IEnumerable<Future> futures, bool eagerError = false, Action<T> cleanUp = null) {
_Future result = new _Future();
List<T> values = null; // Collects the values. Set to null on error.
int remaining = 0; // How many futures are we waiting for.
Exception error = null; // The first error from a future.
Func<Exception, FutureOr> handleError = (Exception theError) => {
remaining--;
if (values != null) {
if (cleanUp != null) {
foreach (var value in values) {
if (value != null) {
// Ensure errors from cleanUp are uncaught.
Future.sync(() => {
cleanUp(value);
return FutureOr.nullValue;
});
}
}
}
values = null;
if (remaining == 0 || eagerError) {
result._completeError(theError);
}
else {
error = theError;
}
}
else if (remaining == 0 && !eagerError) {
result._completeError(error);
}
return FutureOr.nullValue;
};
try {
// As each future completes, put its value into the corresponding
// position in the list of values.
foreach (var future in futures) {
int pos = remaining;
future.then((object value) => {
remaining--;
if (values != null) {
values[pos] = (T) value;
if (remaining == 0) {
result._completeWithValue(values);
}
}
else {
if (cleanUp != null && value != null) {
// Ensure errors from cleanUp are uncaught.
Future.sync(() => {
cleanUp((T) value);
return FutureOr.nullValue;
});
}
if (remaining == 0 && !eagerError) {
result._completeError(error);
}
}
return FutureOr.nullValue;
}, onError: handleError);
// Increment the 'remaining' after the call to 'then'.
// If that call throws, we don't expect any future callback from
// the future, and we also don't increment remaining.
remaining++;
}
if (remaining == 0) {
return Future.value(FutureOr.withValue(new List<T>()));
}
values = new List<T>(remaining);
}
catch (Exception e) {
// The error must have been thrown while iterating over the futures
// list, or while installing a callback handler on the future.
if (remaining == 0 || eagerError) {
// Throw a new Future.error.
// Don't just call `result._completeError` since that would propagate
// the error too eagerly, not giving the callers time to install
// error handlers.
// Also, don't use `_asyncCompleteError` since that one doesn't give
// zones the chance to intercept the error.
return Future.error(e);
}
else {
// Don't allocate a list for values, thus indicating that there was an
// error.
// Set error to the caught exception.
error = e;
}
}
return result;
}
public static Future any(IEnumerable<Future> futures) {
var completer = Completer.sync();
Func<object, FutureOr> onValue = (object value) => {
if (!completer.isCompleted) completer.complete(FutureOr.withValue(value));
return FutureOr.nullValue;
};
Func<Exception, FutureOr> onError = (Exception error) => {
if (!completer.isCompleted) completer.completeError(error);
return FutureOr.nullValue;
};
foreach (var future in futures) {
future.then(onValue, onError: onError);
}
return completer.future;
}
public static Future forEach<T>(IEnumerable<T> elements, Func<T, FutureOr> action) {
var iterator = elements.GetEnumerator();
return doWhile(() => {
if (!iterator.MoveNext()) return FutureOr.falseValue;
var result = action(iterator.Current);
if (result.isFuture) return FutureOr.withFuture(result.future.then(_kTrue));
return FutureOr.trueValue;
});
}
static readonly Func<object, FutureOr> _kTrue = (_) => FutureOr.trueValue;
public static Future doWhile(Func<FutureOr> action) {
_Future doneSignal = new _Future();
ZoneUnaryCallback nextIteration = null;
// Bind this callback explicitly so that each iteration isn't bound in the
// context of all the previous iterations' callbacks.
// This avoids, e.g., deeply nested stack traces from the stack trace
// package.
nextIteration = Zone.current.bindUnaryCallbackGuarded((object keepGoingObj) => {
bool keepGoing = (bool) keepGoingObj;
while (keepGoing) {
FutureOr result;
try {
result = action();
}
catch (Exception error) {
// Cannot use _completeWithErrorCallback because it completes
// the future synchronously.
async_._asyncCompleteWithErrorCallback(doneSignal, error);
return null;
}
if (result.isFuture) {
result.future.then((value) => {
nextIteration((bool) value);
return FutureOr.nullValue;
}, onError: error => {
doneSignal._completeError(error);
return FutureOr.nullValue;
});
return null;
}
keepGoing = (bool) result.value;
}
doneSignal._complete(FutureOr.nullValue);
return null;
});
nextIteration(true);
return doneSignal;
}
public abstract Future then(Func<object, FutureOr> onValue, Func<Exception, FutureOr> onError = null);
public abstract Future catchError(Func<Exception, FutureOr> onError, Func<Exception, bool> test = null);
public abstract Future whenComplete(Func<object> action);
// public abstract Stream asStream();
public abstract Future timeout(TimeSpan timeLimit, Func<FutureOr> onTimeout = null);
}
public class TimeoutException : Exception {
public readonly TimeSpan? duration;
public TimeoutException(string message, TimeSpan? duration = null) : base(message) {
this.duration = duration;
}
public override string ToString() {
string result = "TimeoutException";
if (duration != null) result = $"TimeoutException after {duration}";
if (Message != null) result = $"result: {Message}";
return result;
}
}
public abstract class Completer {
public static Completer create() => new _AsyncCompleter();
public static Completer sync() => new _SyncCompleter();
public abstract Future future { get; }
public abstract void complete(FutureOr value = default);
public abstract void completeError(Exception error);
public abstract bool isCompleted { get; }
}
public static partial class async_ {
internal static void _completeWithErrorCallback(_Future result, Exception error) {
AsyncError replacement = Zone.current.errorCallback(error);
if (replacement != null) {
error = _nonNullError(replacement.InnerException);
}
result._completeError(error);
}
internal static void _asyncCompleteWithErrorCallback(_Future result, Exception error) {
AsyncError replacement = Zone.current.errorCallback(error);
if (replacement != null) {
error = _nonNullError(replacement.InnerException);
}
result._asyncCompleteError(error);
}
internal static Exception _nonNullError(Exception error) =>
error ?? new Exception("Throw of null.");
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bdf10194e91e4558bc97ebad8b81d5a0
timeCreated: 1599458114

Просмотреть файл

@ -0,0 +1,812 @@
using System;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.async2 {
using _FutureOnValue = Func<object, FutureOr>;
using _FutureErrorTest = Func<Exception, bool>;
using _FutureAction = Func<object>;
abstract class _Completer : Completer {
protected readonly _Future _future = new _Future();
public override Future future => _future;
public override void completeError(Exception error) {
if (error == null)
throw new ArgumentNullException(nameof(error));
if (!_future._mayComplete) throw new Exception("Future already completed");
AsyncError replacement = Zone.current.errorCallback(error);
if (replacement != null) {
error = async_._nonNullError(replacement.InnerException);
}
_completeError(error);
}
protected abstract void _completeError(Exception error);
public override bool isCompleted => !_future._mayComplete;
}
class _AsyncCompleter : _Completer {
public override void complete(FutureOr value = default) {
if (!_future._mayComplete) throw new Exception("Future already completed");
_future._asyncComplete(value);
}
protected override void _completeError(Exception error) {
_future._asyncCompleteError(error);
}
}
class _SyncCompleter : _Completer {
public override void complete(FutureOr value = default) {
if (!_future._mayComplete) throw new Exception("Future already completed");
_future._complete(value);
}
protected override void _completeError(Exception error) {
_future._completeError(error);
}
}
class _FutureListener {
public const int maskValue = 1;
public const int maskError = 2;
public const int maskTestError = 4;
public const int maskWhencomplete = 8;
public const int stateChain = 0;
public const int stateThen = maskValue;
public const int stateThenOnerror = maskValue | maskError;
public const int stateCatcherror = maskError;
public const int stateCatcherrorTest = maskError | maskTestError;
public const int stateWhencomplete = maskWhencomplete;
public const int maskType = maskValue | maskError | maskTestError | maskWhencomplete;
public const int stateIsAwait = 16;
internal _FutureListener _nextListener;
public readonly _Future result;
public readonly int state;
public readonly Delegate callback;
public readonly Func<Exception, FutureOr> errorCallback;
_FutureListener(_Future result, Delegate callback, Func<Exception, FutureOr> errorCallback, int state) {
this.result = result;
this.state = state;
this.callback = callback;
this.errorCallback = errorCallback;
}
public static _FutureListener then(
_Future result, _FutureOnValue onValue, Func<Exception, FutureOr> errorCallback) {
return new _FutureListener(
result, onValue, errorCallback,
(errorCallback == null) ? stateThen : stateThenOnerror
);
}
public static _FutureListener thenAwait(
_Future result, _FutureOnValue onValue, Func<Exception, FutureOr> errorCallback) {
return new _FutureListener(
result, onValue, errorCallback,
((errorCallback == null) ? stateThen : stateThenOnerror) | stateIsAwait
);
}
public static _FutureListener catchError(_Future result, Func<Exception, FutureOr> errorCallback,
_FutureErrorTest callback) {
return new _FutureListener(
result, callback, errorCallback,
(callback == null) ? stateCatcherror : stateCatcherrorTest
);
}
public static _FutureListener whenComplete(_Future result, _FutureAction callback) {
return new _FutureListener(
result, callback, null,
stateWhencomplete
);
}
internal Zone _zone => result._zone;
public bool handlesValue => (state & maskValue) != 0;
public bool handlesError => (state & maskError) != 0;
public bool hasErrorTest => (state & maskType) == stateCatcherrorTest;
public bool handlesComplete => (state & maskType) == stateWhencomplete;
public bool isAwait => (state & stateIsAwait) != 0;
internal _FutureOnValue _onValue {
get {
D.assert(handlesValue);
return (_FutureOnValue) callback;
}
}
internal Func<Exception, FutureOr> _onError => errorCallback;
internal _FutureErrorTest _errorTest {
get {
D.assert(hasErrorTest);
return (_FutureErrorTest) callback;
}
}
internal _FutureAction _whenCompleteAction {
get {
D.assert(handlesComplete);
return (_FutureAction) callback;
}
}
public bool hasErrorCallback {
get {
D.assert(handlesError);
return _onError != null;
}
}
public FutureOr handleValue(object sourceResult) {
return (FutureOr) _zone.runUnary(arg => _onValue(arg), sourceResult);
}
public bool matchesErrorTest(AsyncError asyncError) {
if (!hasErrorTest) return true;
return (bool) _zone.runUnary(arg => _errorTest((Exception) arg), asyncError.InnerException);
}
public FutureOr handleError(AsyncError asyncError) {
D.assert(handlesError && hasErrorCallback);
var errorCallback = this.errorCallback;
return (FutureOr) _zone.runUnary(arg => errorCallback((Exception) arg), asyncError.InnerException);
}
public object handleWhenComplete() {
D.assert(!handlesError);
return _zone.run(() => _whenCompleteAction());
}
}
public class _Future : Future {
internal const int _stateIncomplete = 0;
internal const int _statePendingComplete = 1;
internal const int _stateChained = 2;
internal const int _stateValue = 4;
internal const int _stateError = 8;
internal int _state = _stateIncomplete;
internal readonly Zone _zone;
internal object _resultOrListeners;
internal _Future() {
_zone = Zone.current;
}
internal _Future(Zone zone) {
_zone = zone;
}
internal static _Future immediate(object result) {
var future = new _Future(Zone.current);
future._asyncComplete(result);
return future;
}
internal static _Future zoneValue(object value, Zone zone) {
var future = new _Future(zone);
future._setValue(value);
return future;
}
internal static _Future immediateError(Exception error) {
var future = new _Future(Zone.current);
future._asyncCompleteError(error);
return future;
}
internal static _Future value(object value) {
return zoneValue(value, Zone.current);
}
internal bool _mayComplete => _state == _stateIncomplete;
internal bool _isPendingComplete => _state == _statePendingComplete;
internal bool _mayAddListener => _state <= _statePendingComplete;
internal bool _isChained => _state == _stateChained;
internal bool _isComplete => _state >= _stateValue;
internal bool _hasError => _state == _stateError;
internal void _setChained(_Future source) {
D.assert(_mayAddListener);
_state = _stateChained;
_resultOrListeners = source;
}
public override Future then(Func<object, FutureOr> f, Func<Exception, FutureOr> onError = null) {
Zone currentZone = Zone.current;
if (!ReferenceEquals(currentZone, async_._rootZone)) {
f = async_._registerUnaryHandler(f, currentZone);
if (onError != null) {
onError = async_._registerErrorHandler(onError, currentZone);
}
}
_Future result = new _Future();
_addListener(_FutureListener.then(result, f, onError));
return result;
}
public override Future catchError(Func<Exception, FutureOr> onError, Func<Exception, bool> test = null) {
_Future result = new _Future();
if (!ReferenceEquals(result._zone, async_._rootZone)) {
onError = async_._registerErrorHandler(onError, result._zone);
if (test != null) {
test = async_._registerUnaryHandler(test, result._zone);
}
}
_addListener(_FutureListener.catchError(result, onError, test));
return result;
}
public override Future whenComplete(Func<object> action) {
_Future result = new _Future();
if (!ReferenceEquals(result._zone, async_._rootZone)) {
action = async_._registerHandler(action, result._zone);
}
_addListener(_FutureListener.whenComplete(result, action));
return result;
}
// Stream<T> asStream() => new Stream<T>.fromFuture(this);
internal void _setPendingComplete() {
D.assert(_mayComplete);
_state = _statePendingComplete;
}
internal void _clearPendingComplete() {
D.assert(_isPendingComplete);
_state = _stateIncomplete;
}
internal AsyncError _error {
get {
D.assert(_hasError);
return (AsyncError) _resultOrListeners;
}
}
internal _Future _chainSource {
get {
D.assert(_isChained);
return (_Future) _resultOrListeners;
}
}
internal void _setValue(object value) {
D.assert(!_isComplete); // But may have a completion pending.
_state = _stateValue;
_resultOrListeners = value;
}
internal void _setErrorObject(AsyncError error) {
D.assert(!_isComplete); // But may have a completion pending.
_state = _stateError;
_resultOrListeners = error;
}
internal void _setError(Exception error) {
_setErrorObject(new AsyncError(error));
}
internal void _cloneResult(_Future source) {
D.assert(!_isComplete);
D.assert(source._isComplete);
_state = source._state;
_resultOrListeners = source._resultOrListeners;
}
internal void _addListener(_FutureListener listener) {
D.assert(listener._nextListener == null);
if (_mayAddListener) {
listener._nextListener = (_FutureListener) _resultOrListeners;
_resultOrListeners = listener;
}
else {
if (_isChained) {
// Delegate listeners to chained source future.
// If the source is complete, instead copy its values and
// drop the chaining.
_Future source = _chainSource;
if (!source._isComplete) {
source._addListener(listener);
return;
}
_cloneResult(source);
}
D.assert(_isComplete);
// Handle late listeners asynchronously.
_zone.scheduleMicrotask(() => {
_propagateToListeners(this, listener);
return null;
});
}
}
void _prependListeners(_FutureListener listeners) {
if (listeners == null) return;
if (_mayAddListener) {
_FutureListener existingListeners = (_FutureListener) _resultOrListeners;
_resultOrListeners = listeners;
if (existingListeners != null) {
_FutureListener cursor = listeners;
while (cursor._nextListener != null) {
cursor = cursor._nextListener;
}
cursor._nextListener = existingListeners;
}
}
else {
if (_isChained) {
// Delegate listeners to chained source future.
// If the source is complete, instead copy its values and
// drop the chaining.
_Future source = _chainSource;
if (!source._isComplete) {
source._prependListeners(listeners);
return;
}
_cloneResult(source);
}
D.assert(_isComplete);
listeners = _reverseListeners(listeners);
_zone.scheduleMicrotask(() => {
_propagateToListeners(this, listeners);
return null;
});
}
}
_FutureListener _removeListeners() {
// Reverse listeners before returning them, so the resulting list is in
// subscription order.
D.assert(!_isComplete);
_FutureListener current = (_FutureListener) _resultOrListeners;
_resultOrListeners = null;
return _reverseListeners(current);
}
_FutureListener _reverseListeners(_FutureListener listeners) {
_FutureListener prev = null;
_FutureListener current = listeners;
while (current != null) {
_FutureListener next = current._nextListener;
current._nextListener = prev;
prev = current;
current = next;
}
return prev;
}
static void _chainForeignFuture(Future source, _Future target) {
D.assert(!target._isComplete);
D.assert(!(source is _Future));
// Mark the target as chained (and as such half-completed).
target._setPendingComplete();
try {
source.then((value) => {
D.assert(target._isPendingComplete);
// The "value" may be another future if the foreign future
// implementation is mis-behaving,
// so use _complete instead of _completeWithValue.
target._clearPendingComplete(); // Clear this first, it's set again.
target._complete(FutureOr.withValue(value));
return new FutureOr();
},
onError: (Exception error) => {
D.assert(target._isPendingComplete);
target._completeError(error);
return new FutureOr();
});
}
catch (Exception e) {
// This only happens if the `then` call threw synchronously when given
// valid arguments.
// That requires a non-conforming implementation of the Future interface,
// which should, hopefully, never happen.
async_.scheduleMicrotask(() => {
target._completeError(e);
return null;
});
}
}
static void _chainCoreFuture(_Future source, _Future target) {
D.assert(target._mayAddListener); // Not completed, not already chained.
while (source._isChained) {
source = source._chainSource;
}
if (source._isComplete) {
_FutureListener listeners = target._removeListeners();
target._cloneResult(source);
_propagateToListeners(target, listeners);
}
else {
_FutureListener listeners = (_FutureListener) target._resultOrListeners;
target._setChained(source);
source._prependListeners(listeners);
}
}
internal void _complete(FutureOr value) {
D.assert(!_isComplete);
if (value.isFuture) {
if (value.future is _Future coreFuture) {
_chainCoreFuture(coreFuture, this);
}
else {
_chainForeignFuture(value.future, this);
}
}
else {
_FutureListener listeners = _removeListeners();
_setValue(value);
_propagateToListeners(this, listeners);
}
}
internal void _completeWithValue(object value) {
D.assert(!_isComplete);
D.assert(!(value is Future));
_FutureListener listeners = _removeListeners();
_setValue(value);
_propagateToListeners(this, listeners);
}
internal void _completeError(Exception error) {
D.assert(!_isComplete);
_FutureListener listeners = _removeListeners();
_setError(error);
_propagateToListeners(this, listeners);
}
internal void _asyncComplete(object value) {
D.assert(!_isComplete);
// Two corner cases if the value is a future:
// 1. the future is already completed and an error.
// 2. the future is not yet completed but might become an error.
// The first case means that we must not immediately complete the Future,
// as our code would immediately start propagating the error without
// giving the time to install error-handlers.
// However the second case requires us to deal with the value immediately.
// Otherwise the value could complete with an error and report an
// unhandled error, even though we know we are already going to listen to
// it.
if (value is Future future) {
_chainFuture(future);
return;
}
_setPendingComplete();
_zone.scheduleMicrotask(() => {
_completeWithValue(value);
return null;
});
}
internal void _chainFuture(Future value) {
if (value is _Future future) {
if (future._hasError) {
// Delay completion to allow the user to register callbacks.
_setPendingComplete();
_zone.scheduleMicrotask(() => {
_chainCoreFuture(future, this);
return null;
});
}
else {
_chainCoreFuture(future, this);
}
return;
}
// Just listen on the foreign future. This guarantees an async delay.
_chainForeignFuture(value, this);
}
internal void _asyncCompleteError(Exception error) {
D.assert(!_isComplete);
_setPendingComplete();
_zone.scheduleMicrotask(() => {
_completeError(error);
return null;
});
}
static void _propagateToListeners(_Future source, _FutureListener listeners) {
while (true) {
D.assert(source._isComplete);
bool hasError = source._hasError;
if (listeners == null) {
if (hasError) {
AsyncError asyncError = source._error;
source._zone.handleUncaughtError(asyncError);
}
return;
}
// Usually futures only have one listener. If they have several, we
// call handle them separately in recursive calls, continuing
// here only when there is only one listener left.
while (listeners._nextListener != null) {
_FutureListener currentListener = listeners;
listeners = currentListener._nextListener;
currentListener._nextListener = null;
_propagateToListeners(source, currentListener);
}
_FutureListener listener = listeners;
var sourceResult = source._resultOrListeners;
// Do the actual propagation.
// Set initial state of listenerHasError and listenerValueOrError. These
// variables are updated with the outcome of potential callbacks.
// Non-error results, including futures, are stored in
// listenerValueOrError and listenerHasError is set to false. Errors
// are stored in listenerValueOrError as an [AsyncError] and
// listenerHasError is set to true.
bool listenerHasError = hasError;
var listenerValueOrError = sourceResult;
// Only if we either have an error or callbacks, go into this, somewhat
// expensive, branch. Here we'll enter/leave the zone. Many futures
// don't have callbacks, so this is a significant optimization.
if (hasError || listener.handlesValue || listener.handlesComplete) {
Zone zone = listener._zone;
if (hasError && !source._zone.inSameErrorZone(zone)) {
// Don't cross zone boundaries with errors.
AsyncError asyncError = source._error;
source._zone.handleUncaughtError(asyncError);
return;
}
Zone oldZone = null;
if (!ReferenceEquals(Zone.current, zone)) {
// Change zone if it's not current.
oldZone = Zone._enter(zone);
}
// These callbacks are abstracted to isolate the try/catch blocks
// from the rest of the code to work around a V8 glass jaw.
Action handleWhenCompleteCallback = () => {
// The whenComplete-handler is not combined with normal value/error
// handling. This means at most one handleX method is called per
// listener.
D.assert(!listener.handlesValue);
D.assert(!listener.handlesError);
object completeResult = null;
try {
completeResult = listener.handleWhenComplete();
}
catch (Exception e) {
if (hasError && ReferenceEquals(source._error.InnerException, e)) {
listenerValueOrError = source._error;
}
else {
listenerValueOrError = new AsyncError(e);
}
listenerHasError = true;
return;
}
if (completeResult is Future completeResultFuture) {
if (completeResult is _Future completeResultCoreFuture &&
completeResultCoreFuture._isComplete) {
if (completeResultCoreFuture._hasError) {
listenerValueOrError = completeResultCoreFuture._error;
listenerHasError = true;
}
// Otherwise use the existing result of source.
return;
}
// We have to wait for the completeResult future to complete
// before knowing if it's an error or we should use the result
// of source.
var originalSource = source;
listenerValueOrError =
completeResultFuture.then((_) => FutureOr.withFuture(originalSource));
listenerHasError = false;
}
};
Action handleValueCallback = () => {
try {
listenerValueOrError = listener.handleValue(sourceResult);
}
catch (Exception e) {
listenerValueOrError = new AsyncError(e);
listenerHasError = true;
}
};
Action handleError = () => {
try {
AsyncError asyncError = source._error;
if (listener.matchesErrorTest(asyncError) &&
listener.hasErrorCallback) {
listenerValueOrError = listener.handleError(asyncError);
listenerHasError = false;
}
}
catch (Exception e) {
if (ReferenceEquals(source._error.InnerException, e)) {
listenerValueOrError = source._error;
}
else {
listenerValueOrError = new AsyncError(e);
}
listenerHasError = true;
}
};
if (listener.handlesComplete) {
handleWhenCompleteCallback();
}
else if (!hasError) {
if (listener.handlesValue) {
handleValueCallback();
}
}
else {
if (listener.handlesError) {
handleError();
}
}
// If we changed zone, oldZone will not be null.
if (oldZone != null) Zone._leave(oldZone);
// If the listener's value is a future we need to chain it. Note that
// this can only happen if there is a callback.
if (listenerValueOrError is Future chainSource) {
// Shortcut if the chain-source is already completed. Just continue
// the loop.
_Future listenerResult = listener.result;
if (chainSource is _Future chainSourceCore) {
if (chainSourceCore._isComplete) {
listeners = listenerResult._removeListeners();
listenerResult._cloneResult(chainSourceCore);
source = chainSourceCore;
continue;
}
else {
_chainCoreFuture(chainSourceCore, listenerResult);
}
}
else {
_chainForeignFuture(chainSource, listenerResult);
}
return;
}
}
_Future result = listener.result;
listeners = result._removeListeners();
if (!listenerHasError) {
result._setValue(listenerValueOrError);
}
else {
AsyncError asyncError = (AsyncError) listenerValueOrError;
result._setErrorObject(asyncError);
}
// Prepare for next round.
source = result;
}
}
public override Future timeout(TimeSpan timeLimit, Func<FutureOr> onTimeout = null) {
if (_isComplete) return _Future.immediate(this);
_Future result = new _Future();
Timer timer;
if (onTimeout == null) {
timer = Timer.create(timeLimit, () => {
result._completeError(
new TimeoutException("Future not completed", timeLimit));
return null;
});
}
else {
Zone zone = Zone.current;
onTimeout = async_._registerHandler(onTimeout, zone);
timer = Timer.create(timeLimit, () => {
try {
result._complete((FutureOr) zone.run(() => onTimeout()));
}
catch (Exception e) {
result._completeError(e);
}
return null;
});
}
then(v => {
if (timer.isActive) {
timer.cancel();
result._completeWithValue(v);
}
return FutureOr.nullValue;
}, onError: e => {
if (timer.isActive) {
timer.cancel();
result._completeError(e);
}
return FutureOr.nullValue;
});
return result;
}
}
public static partial class async_ {
internal static Func<object> _registerHandler(Func<object> handler, Zone zone) {
var callback = zone.registerCallback(() => handler());
return () => callback();
}
internal static Func<FutureOr> _registerHandler(Func<FutureOr> handler, Zone zone) {
var callback = zone.registerCallback(() => handler());
return () => (FutureOr) callback();
}
internal static Func<object, FutureOr> _registerUnaryHandler(Func<object, FutureOr> handler, Zone zone) {
var callback = zone.registerUnaryCallback(arg => handler(arg));
return arg => (FutureOr) callback(arg);
}
internal static Func<Exception, bool> _registerUnaryHandler(Func<Exception, bool> handler, Zone zone) {
var callback = zone.registerUnaryCallback(arg => handler((Exception) arg));
return arg => (bool) callback(arg);
}
internal static Func<Exception, FutureOr> _registerErrorHandler(Func<Exception, FutureOr> errorHandler,
Zone zone) {
var callback = zone.registerUnaryCallback(arg => errorHandler((Exception) arg));
return arg => (FutureOr) callback(arg);
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4072406cc46448f99159c137b458841e
timeCreated: 1599458114

Просмотреть файл

@ -0,0 +1,129 @@
using System;
using System.Runtime.InteropServices;
using AOT;
using Unity.UIWidgets.ui2;
namespace Unity.UIWidgets.async2 {
class _AsyncCallbackEntry {
public readonly ZoneCallback callback;
public _AsyncCallbackEntry next;
internal _AsyncCallbackEntry(ZoneCallback callback) {
this.callback = callback;
}
}
public static partial class async_ {
static _AsyncCallbackEntry _nextCallback;
static _AsyncCallbackEntry _lastCallback;
static _AsyncCallbackEntry _lastPriorityCallback;
static bool _isInCallbackLoop = false;
static void _microtaskLoop() {
while (_nextCallback != null) {
_lastPriorityCallback = null;
_AsyncCallbackEntry entry = _nextCallback;
_nextCallback = entry.next;
if (_nextCallback == null) _lastCallback = null;
entry.callback();
}
}
static object _startMicrotaskLoop() {
_isInCallbackLoop = true;
try {
// Moved to separate function because try-finally prevents
// good optimization.
_microtaskLoop();
}
finally {
_lastPriorityCallback = null;
_isInCallbackLoop = false;
if (_nextCallback != null) {
_AsyncRun._scheduleImmediate(_startMicrotaskLoop);
}
}
return null;
}
static void _scheduleAsyncCallback(ZoneCallback callback) {
_AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback);
if (_nextCallback == null) {
_nextCallback = _lastCallback = newEntry;
if (!_isInCallbackLoop) {
_AsyncRun._scheduleImmediate(_startMicrotaskLoop);
}
}
else {
_lastCallback.next = newEntry;
_lastCallback = newEntry;
}
}
static void _schedulePriorityAsyncCallback(ZoneCallback callback) {
if (_nextCallback == null) {
_scheduleAsyncCallback(callback);
_lastPriorityCallback = _lastCallback;
return;
}
_AsyncCallbackEntry entry = new _AsyncCallbackEntry(callback);
if (_lastPriorityCallback == null) {
entry.next = _nextCallback;
_nextCallback = _lastPriorityCallback = entry;
}
else {
entry.next = _lastPriorityCallback.next;
_lastPriorityCallback.next = entry;
_lastPriorityCallback = entry;
if (entry.next == null) {
_lastCallback = entry;
}
}
}
public static void scheduleMicrotask(ZoneCallback callback) {
_Zone currentZone = (_Zone) Zone.current;
if (ReferenceEquals(_rootZone, currentZone)) {
// No need to bind the callback. We know that the root's scheduleMicrotask
// will be invoked in the root zone.
_rootScheduleMicrotask(null, null, _rootZone, callback);
return;
}
_ZoneFunction<ScheduleMicrotaskHandler> implementation = currentZone._scheduleMicrotask;
if (ReferenceEquals(_rootZone, implementation.zone) &&
_rootZone.inSameErrorZone(currentZone)) {
_rootScheduleMicrotask(
null, null, currentZone, currentZone.registerCallback(callback));
return;
}
Zone.current.scheduleMicrotask(Zone.current.bindCallbackGuarded(callback));
}
}
class _AsyncRun {
internal static void _scheduleImmediate(ZoneCallback callback) {
GCHandle callabackHandle = GCHandle.Alloc(callback);
UIMonoState_scheduleMicrotask(_scheduleMicrotask, (IntPtr) callabackHandle);
}
[MonoPInvokeCallback(typeof(UIMonoState_scheduleMicrotaskCallback))]
static void _scheduleMicrotask(IntPtr callbackHandle) {
GCHandle handle = (GCHandle) callbackHandle;
var callback = (ZoneCallback) handle.Target;
handle.Free();
callback();
}
delegate void UIMonoState_scheduleMicrotaskCallback(IntPtr callbackHandle);
[DllImport(NativeBindings.dllName)]
static extern void UIMonoState_scheduleMicrotask(UIMonoState_scheduleMicrotaskCallback callback,
IntPtr callbackHandle);
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e9d7bdadaa9c46e0b96ba19f4261185a
timeCreated: 1599458114

Просмотреть файл

@ -0,0 +1,142 @@
using System;
using System.Runtime.InteropServices;
using AOT;
using Unity.UIWidgets.ui2;
namespace Unity.UIWidgets.async2 {
public abstract class Timer : IDisposable {
public static Timer create(TimeSpan duration, ZoneCallback callback) {
if (Zone.current == Zone.root) {
return Zone.current.createTimer(duration, callback);
}
return Zone.current
.createTimer(duration, Zone.current.bindCallbackGuarded(callback));
}
public static Timer periodic(TimeSpan duration, ZoneUnaryCallback callback) {
if (Zone.current == Zone.root) {
return Zone.current.createPeriodicTimer(duration, callback);
}
var boundCallback = Zone.current.bindUnaryCallbackGuarded(callback);
return Zone.current.createPeriodicTimer(duration, boundCallback);
}
public static void run(ZoneCallback callback) {
Timer.create(TimeSpan.Zero, callback);
}
public abstract void cancel();
public void Dispose() {
cancel();
}
public abstract int tick { get; }
public abstract bool isActive { get; }
internal static Timer _createTimer(TimeSpan duration, ZoneCallback callback) {
return _Timer._createTimer(_ => callback(), (int) duration.TotalMilliseconds, false);
}
internal static Timer _createPeriodicTimer(
TimeSpan duration, ZoneUnaryCallback callback) {
return _Timer._createTimer(callback, (int) duration.TotalMilliseconds, true);
}
}
class _Timer : Timer {
int _tick = 0;
ZoneUnaryCallback _callback;
int _wakeupTime;
readonly int _milliSeconds;
readonly bool _repeating;
_Timer(ZoneUnaryCallback callback, int wakeupTime, int milliSeconds, bool repeating) {
_callback = callback;
_wakeupTime = wakeupTime;
_milliSeconds = milliSeconds;
_repeating = repeating;
}
internal static _Timer _createTimer(ZoneUnaryCallback callback, int milliSeconds, bool repeating) {
if (milliSeconds < 0) {
milliSeconds = 0;
}
int now = UIMonoState_timerMillisecondClock();
int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds);
_Timer timer = new _Timer(callback, wakeupTime, milliSeconds, repeating);
timer._enqueue();
return timer;
}
public override void cancel() {
_callback = null;
}
public override bool isActive => _callback != null;
public override int tick => _tick;
void _advanceWakeupTime() {
if (_milliSeconds > 0) {
_wakeupTime += _milliSeconds;
}
else {
_wakeupTime = UIMonoState_timerMillisecondClock();
}
}
void _enqueue() {
GCHandle callabackHandle = GCHandle.Alloc(this);
UIMonoState_postTaskForTime(_postTaskForTime, (IntPtr) callabackHandle, _wakeupTime * 1000L);
}
[MonoPInvokeCallback(typeof(UIMonoState_postTaskForTimeCallback))]
static void _postTaskForTime(IntPtr callbackHandle) {
GCHandle timerHandle = (GCHandle) callbackHandle;
var timer = (_Timer) timerHandle.Target;
timerHandle.Free();
if (timer._callback != null) {
var callback = timer._callback;
if (!timer._repeating) {
timer._callback = null;
}
else if (timer._milliSeconds > 0) {
var ms = timer._milliSeconds;
int overdue = UIMonoState_timerMillisecondClock() - timer._wakeupTime;
if (overdue > ms) {
int missedTicks = overdue / ms;
timer._wakeupTime += missedTicks * ms;
timer._tick += missedTicks;
}
}
timer._tick += 1;
callback(timer);
if (timer._repeating && (timer._callback != null)) {
timer._advanceWakeupTime();
timer._enqueue();
}
}
}
[DllImport(NativeBindings.dllName)]
static extern int UIMonoState_timerMillisecondClock();
delegate void UIMonoState_postTaskForTimeCallback(IntPtr callbackHandle);
[DllImport(NativeBindings.dllName)]
static extern void UIMonoState_postTaskForTime(UIMonoState_postTaskForTimeCallback callback,
IntPtr callbackHandle, long targetTimeNanos);
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dc44ad1967714937b709d5f22c6b3bec
timeCreated: 1599458114

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d8dbf866190b44e6a47e0ff7c0e2bdc4
timeCreated: 1599458114

Просмотреть файл

@ -25,7 +25,7 @@ namespace Unity.UIWidgets.debugger {
}
public bool debugEnabled {
get { return D.debugEnabled; }
get { return foundation_.kDebugMode; }
}
public void close() {

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 32b343b53c4d484f8c149742f9d0a919
timeCreated: 1599028965

Просмотреть файл

@ -0,0 +1,8 @@
using System.Collections;
namespace developer {
public static partial class developer_ {
public static void postEvent(string eventKind, Hashtable eventData) {
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2dcff701560471ab813d010a98a7d36
timeCreated: 1599030815

Просмотреть файл

@ -0,0 +1,11 @@
using System.Collections;
namespace developer {
public class Timeline {
public static void startSync(string name, Hashtable arguments = null) {
}
public static void finishSync() {
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 263dee475b944a33a99e0d8925f904c4
timeCreated: 1599028971

Просмотреть файл

@ -51,6 +51,7 @@ namespace Unity.UIWidgets.engine2 {
}
protected virtual void main() {
Debug.Log(Debug.isDebugBuild);
}
public void entryPoint() {

Просмотреть файл

@ -1,2 +1,55 @@
namespace Unity.UIWidgets.foundation {
using System;
using RSG;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.ui2;
namespace Unity.UIWidgets.foundation {
public abstract class BindingBase {
protected BindingBase() {
D.assert(!_debugInitialized);
initInstances();
D.assert(_debugInitialized);
}
static bool _debugInitialized = false;
public Window window => Window.instance;
protected virtual void initInstances() {
D.assert(!_debugInitialized);
D.assert(() => {
_debugInitialized = true;
return true;
});
}
protected bool locked => _lockCount > 0;
int _lockCount = 0;
protected Future lockEvents(Func<Future> callback) {
developer.Timeline.startSync("Lock events");
D.assert(callback != null);
_lockCount += 1;
Future future = callback();
D.assert(future != null,
() =>
"The lockEvents() callback returned null; " +
"it should return a Promise that completes when the lock is to expire.");
future.whenComplete(() => {
_lockCount -= 1;
if (!locked) {
developer.Timeline.finishSync();
unlocked();
}
return null;
});
return future;
}
protected virtual void unlocked() {
D.assert(!locked);
}
}
}

Просмотреть файл

@ -1,7 +1,23 @@
using UnityEngine;
namespace Unity.UIWidgets.foundation {
public class FoundationConstants {
public static bool kReleaseMode = !Debug.isDebugBuild;
public static class foundation_ {
public static readonly bool kReleaseMode = !Debug.isDebugBuild;
#if UIWidgets_PROFILE
public const bool kProfileMode = true;
#else
public const bool kProfileMode = false;
#endif
public static readonly bool kDebugMode = !kReleaseMode && !kProfileMode;
public const float precisionErrorTolerance = 1e-10f;
#if UNITY_WEBGL
public const bool kIsWeb = true;
#else
public const bool kIsWeb = false;
#endif
}
}

Просмотреть файл

@ -15,24 +15,14 @@ namespace Unity.UIWidgets.foundation {
Debug.LogException(new AssertionError(message, ex));
}
public static bool debugEnabled {
get {
#if UIWidgets_DEBUG
return true;
#else
return false;
#endif
}
}
[Conditional("UIWidgets_DEBUG")]
[Conditional("UNITY_ASSERTIONS")]
public static void assert(Func<bool> result, Func<string> message = null) {
if (!result()) {
throw new AssertionError(message != null ? message() : "");
}
}
[Conditional("UIWidgets_DEBUG")]
[Conditional("UNITY_ASSERTIONS")]
public static void assert(bool result, Func<string> message = null) {
if (!result) {
throw new AssertionError(message != null ? message() : "");
@ -49,12 +39,6 @@ namespace Unity.UIWidgets.foundation {
public static bool debugPrintRecognizerCallbacksTrace = false;
public static bool debugPrintBeginFrameBanner = false;
public static bool debugPrintEndFrameBanner = false;
public static bool debugPrintScheduleFrameStacks = false;
public static bool debugPaintSizeEnabled = false;
public static bool debugRepaintRainbowEnabled = false;

Просмотреть файл

@ -6,7 +6,7 @@ using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.scheduler2;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
@ -621,7 +621,7 @@ namespace Unity.UIWidgets.material {
this._state.interactionTimer?.cancel();
this._state.interactionTimer = Window.instance.run(
new TimeSpan(0, 0, 0, 0,
(int) (_minimumInteractionTimeMilliSeconds * SchedulerBinding.instance.timeDilation)),
(int) (_minimumInteractionTimeMilliSeconds * scheduler_.timeDilation)),
() => {
this._state.interactionTimer = null;
if (!this._active &&

Просмотреть файл

@ -4,8 +4,9 @@ using System.Linq;
using RSG;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.scheduler2;
using Unity.UIWidgets.ui;
using SchedulerBinding = Unity.UIWidgets.scheduler.SchedulerBinding;
namespace Unity.UIWidgets.painting {
public class ImageInfo : IEquatable<ImageInfo> {
@ -350,7 +351,7 @@ namespace Unity.UIWidgets.painting {
}
TimeSpan delay = this._frameDuration.Value - (timestamp - this._shownTimestamp.Value);
delay = new TimeSpan((long) (delay.Ticks * SchedulerBinding.instance.timeDilation));
delay = new TimeSpan((long) (delay.Ticks * scheduler_.timeDilation));
this._timer = Window.instance.run(delay, this._scheduleAppFrame);
}

Просмотреть файл

@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Text;
using RSG.Promises;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler2;
using Unity.UIWidgets.ui;
using Debug = UnityEngine.Debug;
using FrameCallback = Unity.UIWidgets.ui.FrameCallback;
namespace Unity.UIWidgets.scheduler {
class _FrameCallbackEntry {
@ -173,7 +175,7 @@ namespace Unity.UIWidgets.scheduler {
}
D.assert(() => {
if (D.debugPrintScheduleFrameStacks) {
if (scheduler_.debugPrintScheduleFrameStacks) {
Debug.LogFormat("scheduleFrame() called. Current phase is {0}.", this.schedulerPhase);
}
@ -190,7 +192,7 @@ namespace Unity.UIWidgets.scheduler {
}
D.assert(() => {
if (D.debugPrintScheduleFrameStacks) {
if (scheduler_.debugPrintScheduleFrameStacks) {
Debug.LogFormat("scheduleForcedFrame() called. Current phase is {0}.", this.schedulerPhase);
}
@ -248,7 +250,7 @@ namespace Unity.UIWidgets.scheduler {
});
D.assert(() => {
if (D.debugPrintBeginFrameBanner || D.debugPrintEndFrameBanner) {
if (scheduler_.debugPrintBeginFrameBanner || scheduler_.debugPrintEndFrameBanner) {
var frameTimeStampDescription = new StringBuilder();
if (rawTimeStamp != null) {
_debugDescribeTimeStamp(
@ -259,7 +261,7 @@ namespace Unity.UIWidgets.scheduler {
this._debugBanner =
$"▄▄▄▄▄▄▄▄ Frame {this._profileFrameNumber.ToString().PadRight(7)} {frameTimeStampDescription.ToString().PadLeft(18)} ▄▄▄▄▄▄▄▄";
if (D.debugPrintBeginFrameBanner) {
if (scheduler_.debugPrintBeginFrameBanner) {
Debug.Log(this._debugBanner);
}
}
@ -306,7 +308,7 @@ namespace Unity.UIWidgets.scheduler {
} finally {
this._schedulerPhase = SchedulerPhase.idle;
D.assert(() => {
if (D.debugPrintEndFrameBanner) {
if (scheduler_.debugPrintEndFrameBanner) {
Debug.Log(new string('▀', this._debugBanner.Length));
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1fea1ea4786b4e6bb65b233b11e8b2d5
timeCreated: 1599458322

Просмотреть файл

@ -0,0 +1,674 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using developer;
using Unity.UIWidgets.async;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui2;
using UnityEngine;
using FrameTiming = Unity.UIWidgets.ui2.FrameTiming;
using Timer = Unity.UIWidgets.async2.Timer;
namespace Unity.UIWidgets.scheduler2 {
public static partial class scheduler_ {
public static float timeDilation {
get { return _timeDilation; }
set {
D.assert(value > 0.0f);
if (_timeDilation == value)
return;
SchedulerBinding.instance?.resetEpoch();
_timeDilation = value;
}
}
static float _timeDilation = 1.0f;
}
public delegate void FrameCallback(TimeSpan timeStamp);
public delegate T TaskCallback<out T>();
public delegate bool SchedulingStrategy(int priority = 0, SchedulerBinding scheduler = null);
interface _TaskEntry : IComparable<_TaskEntry> {
int priority { get; }
string debugStack { get; }
void run();
}
class _TaskEntry<T> : _TaskEntry {
internal _TaskEntry(TaskCallback<T> task, int priority) {
this.task = task;
this.priority = priority;
D.assert(() => {
debugStack = StackTraceUtility.ExtractStackTrace();
return true;
});
completer = Completer.create();
}
public readonly TaskCallback<T> task;
public int priority { get; }
public string debugStack { get; private set; }
public Completer completer;
public void run() {
if (!foundation_.kReleaseMode) {
completer.complete(FutureOr.withValue(task()));
}
else {
completer.complete(FutureOr.withValue(task()));
}
}
public int CompareTo(_TaskEntry other) {
return -priority.CompareTo(other.priority);
}
}
class _FrameCallbackEntry {
internal _FrameCallbackEntry(FrameCallback callback, bool rescheduling = false) {
this.callback = callback;
D.assert(() => {
if (rescheduling) {
D.assert(() => {
if (debugCurrentCallbackStack == null) {
throw new UIWidgetsError(
"scheduleFrameCallback called with rescheduling true, but no callback is in scope.\n" +
"The \"rescheduling\" argument should only be set to true if the " +
"callback is being reregistered from within the callback itself, " +
"and only then if the callback itself is entirely synchronous. \n" +
"If this is the initial registration of the callback, or if the " +
"callback is asynchronous, then do not use the \"rescheduling\" " +
"argument.");
}
return true;
});
debugStack = debugCurrentCallbackStack;
}
else {
debugStack = StackTraceUtility.ExtractStackTrace();
}
return true;
});
}
public readonly FrameCallback callback;
public static string debugCurrentCallbackStack;
public string debugStack;
}
public enum SchedulerPhase {
idle,
transientCallbacks,
midFrameMicrotasks,
persistentCallbacks,
postFrameCallbacks,
}
public class SchedulerBinding : BindingBase {
protected override void initInstances() {
base.initInstances();
instance = this;
//SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
readInitialLifecycleStateFromNativeWindow();
if (!foundation_.kReleaseMode) {
int frameNumber = 0;
addTimingsCallback((List<FrameTiming> timings) => {
foreach (FrameTiming frameTiming in timings) {
frameNumber += 1;
_profileFramePostEvent(frameNumber, frameTiming);
}
});
}
}
readonly List<TimingsCallback> _timingsCallbacks = new List<TimingsCallback>();
public void addTimingsCallback(TimingsCallback callback) {
_timingsCallbacks.Add(callback);
if (_timingsCallbacks.Count == 1) {
D.assert(window.onReportTimings == null);
window.onReportTimings = _executeTimingsCallbacks;
}
D.assert(window.onReportTimings == _executeTimingsCallbacks);
}
public void removeTimingsCallback(TimingsCallback callback) {
D.assert(_timingsCallbacks.Contains(callback));
_timingsCallbacks.Remove(callback);
if (_timingsCallbacks.isEmpty()) {
window.onReportTimings = null;
}
}
void _executeTimingsCallbacks(List<FrameTiming> timings) {
List<TimingsCallback> clonedCallbacks =
new List<TimingsCallback>(_timingsCallbacks);
foreach (TimingsCallback callback in clonedCallbacks) {
try {
if (_timingsCallbacks.Contains(callback)) {
callback(timings);
}
}
catch (Exception ex) {
InformationCollector collector = null;
D.assert(() => {
collector = (StringBuilder sb) => {
sb.AppendLine("The TimingsCallback that gets executed was " + callback);
};
return true;
});
UIWidgetsError.reportError(new UIWidgetsErrorDetails(
exception: ex,
context: "while executing callbacks for FrameTiming",
informationCollector: collector
));
}
}
}
public static SchedulerBinding instance {
get { return (SchedulerBinding) Window.instance._binding; }
set { Window.instance._binding = value; }
}
public AppLifecycleState? lifecycleState => _lifecycleState;
AppLifecycleState? _lifecycleState;
protected void readInitialLifecycleStateFromNativeWindow() {
if (_lifecycleState == null) {
handleAppLifecycleStateChanged(_parseAppLifecycleMessage(window.initialLifecycleState));
}
}
protected virtual void handleAppLifecycleStateChanged(AppLifecycleState state) {
_lifecycleState = state;
switch (state) {
case AppLifecycleState.resumed:
case AppLifecycleState.inactive:
_setFramesEnabledState(true);
break;
case AppLifecycleState.paused:
case AppLifecycleState.detached:
_setFramesEnabledState(false);
break;
}
}
static AppLifecycleState _parseAppLifecycleMessage(string message) {
switch (message) {
case "AppLifecycleState.paused":
return AppLifecycleState.paused;
case "AppLifecycleState.resumed":
return AppLifecycleState.resumed;
case "AppLifecycleState.inactive":
return AppLifecycleState.inactive;
case "AppLifecycleState.detached":
return AppLifecycleState.detached;
}
throw new Exception("unknown AppLifecycleState: " + message);
}
public SchedulingStrategy schedulingStrategy = scheduler_.defaultSchedulingStrategy;
readonly PriorityQueue<_TaskEntry> _taskQueue = new PriorityQueue<_TaskEntry>();
public Future scheduleTask<T>(
TaskCallback<T> task,
Priority priority) {
bool isFirstTask = _taskQueue.isEmpty;
_TaskEntry<T> entry = new _TaskEntry<T>(
task,
priority.value
);
_taskQueue.enqueue(entry);
if (isFirstTask && !locked)
_ensureEventLoopCallback();
return entry.completer.future;
}
protected override void unlocked() {
base.unlocked();
if (_taskQueue.isNotEmpty)
_ensureEventLoopCallback();
}
bool _hasRequestedAnEventLoopCallback = false;
void _ensureEventLoopCallback() {
D.assert(!locked);
D.assert(_taskQueue.count != 0);
if (_hasRequestedAnEventLoopCallback)
return;
_hasRequestedAnEventLoopCallback = true;
Timer.run(_runTasks);
}
object _runTasks() {
_hasRequestedAnEventLoopCallback = false;
if (handleEventLoopCallback())
_ensureEventLoopCallback(); // runs next task when there's time
return null;
}
bool handleEventLoopCallback() {
if (_taskQueue.isEmpty || locked)
return false;
_TaskEntry entry = _taskQueue.first;
if (schedulingStrategy(priority: entry.priority, scheduler: this)) {
try {
_taskQueue.removeFirst();
entry.run();
}
catch (Exception exception) {
string callbackStack = null;
D.assert(() => {
callbackStack = entry.debugStack;
return true;
});
UIWidgetsError.reportError(new UIWidgetsErrorDetails(
exception: exception,
library: "scheduler library",
context: "during a task callback",
informationCollector: callbackStack == null
? (InformationCollector) null
: sb => {
sb.AppendLine("\nThis exception was thrown in the context of a scheduler callback. " +
"When the scheduler callback was _registered_ (as opposed to when the " +
"exception was thrown), this was the stack: " + callbackStack);
}
));
}
return _taskQueue.isNotEmpty;
}
return false;
}
int _nextFrameCallbackId = 0;
Dictionary<int, _FrameCallbackEntry> _transientCallbacks = new Dictionary<int, _FrameCallbackEntry>();
readonly HashSet<int> _removedIds = new HashSet<int>();
public int transientCallbackCount => _transientCallbacks.Count;
public int scheduleFrameCallback(FrameCallback callback, bool rescheduling = false) {
scheduleFrame();
_nextFrameCallbackId += 1;
_transientCallbacks[_nextFrameCallbackId] =
new _FrameCallbackEntry(callback, rescheduling: rescheduling);
return _nextFrameCallbackId;
}
public void cancelFrameCallbackWithId(int id) {
D.assert(id > 0);
_transientCallbacks.Remove(id);
_removedIds.Add(id);
}
readonly List<FrameCallback> _persistentCallbacks = new List<FrameCallback>();
public void addPersistentFrameCallback(FrameCallback callback) {
_persistentCallbacks.Add(callback);
}
readonly List<FrameCallback> _postFrameCallbacks = new List<FrameCallback>();
public void addPostFrameCallback(FrameCallback callback) {
_postFrameCallbacks.Add(callback);
}
Completer _nextFrameCompleter;
Future endOfFrame {
get {
if (_nextFrameCompleter == null) {
if (schedulerPhase == SchedulerPhase.idle)
scheduleFrame();
_nextFrameCompleter = Completer.create();
addPostFrameCallback((TimeSpan timeStamp) => {
_nextFrameCompleter.complete();
_nextFrameCompleter = null;
});
}
return _nextFrameCompleter.future;
}
}
public bool hasScheduledFrame => _hasScheduledFrame;
bool _hasScheduledFrame = false;
public SchedulerPhase schedulerPhase => _schedulerPhase;
SchedulerPhase _schedulerPhase = SchedulerPhase.idle;
public bool framesEnabled => _framesEnabled;
bool _framesEnabled = true;
void _setFramesEnabledState(bool enabled) {
if (_framesEnabled == enabled)
return;
_framesEnabled = enabled;
if (enabled)
scheduleFrame();
}
protected void ensureFrameCallbacksRegistered() {
window.onBeginFrame = window.onBeginFrame ?? _handleBeginFrame;
window.onDrawFrame = window.onDrawFrame ?? _handleDrawFrame;
}
public void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
public void scheduleFrame() {
if (_hasScheduledFrame || !_framesEnabled)
return;
D.assert(() => {
if (scheduler_.debugPrintScheduleFrameStacks) {
Debug.LogFormat("scheduleFrame() called. Current phase is {0}.", schedulerPhase);
}
return true;
});
ensureFrameCallbacksRegistered();
Window.instance.scheduleFrame();
_hasScheduledFrame = true;
}
public void scheduleForcedFrame() {
if (!framesEnabled)
return;
if (_hasScheduledFrame)
return;
D.assert(() => {
if (scheduler_.debugPrintScheduleFrameStacks) {
Debug.LogFormat("scheduleForcedFrame() called. Current phase is {0}.", schedulerPhase);
}
return true;
});
ensureFrameCallbacksRegistered();
Window.instance.scheduleFrame();
_hasScheduledFrame = true;
}
bool _warmUpFrame = false;
public void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync("Warm-up frame");
bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() => {
D.assert(_warmUpFrame);
handleBeginFrame(null);
return null;
});
Timer.run(() => {
D.assert(_warmUpFrame);
handleDrawFrame();
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame's timestamp will typically be far in
// the past (the time of the last real frame), so if we didn't reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the "real" frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
return null;
});
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
lockEvents(() => endOfFrame.then(v => {
Timeline.finishSync();
return FutureOr.nullValue;
}));
}
TimeSpan? _firstRawTimeStampInEpoch;
TimeSpan _epochStart = TimeSpan.Zero;
TimeSpan _lastRawTimeStamp = TimeSpan.Zero;
public void resetEpoch() {
_epochStart = _adjustForEpoch(_lastRawTimeStamp);
_firstRawTimeStampInEpoch = null;
}
TimeSpan _adjustForEpoch(TimeSpan rawTimeStamp) {
var rawDurationSinceEpoch = _firstRawTimeStampInEpoch == null
? TimeSpan.Zero
: rawTimeStamp - _firstRawTimeStampInEpoch.Value;
return new TimeSpan((long) (rawDurationSinceEpoch.Ticks / scheduler_.timeDilation) +
_epochStart.Ticks);
}
public TimeSpan currentFrameTimeStamp {
get {
D.assert(_currentFrameTimeStamp != null);
return _currentFrameTimeStamp.Value;
}
}
TimeSpan? _currentFrameTimeStamp;
public TimeSpan currentSystemFrameTimeStamp => _lastRawTimeStamp;
int _debugFrameNumber = 0;
string _debugBanner;
bool _ignoreNextEngineDrawFrame = false;
void _handleBeginFrame(TimeSpan rawTimeStamp) {
if (_warmUpFrame) {
D.assert(!_ignoreNextEngineDrawFrame);
_ignoreNextEngineDrawFrame = true;
return;
}
handleBeginFrame(rawTimeStamp);
}
void _handleDrawFrame() {
if (_ignoreNextEngineDrawFrame) {
_ignoreNextEngineDrawFrame = false;
return;
}
handleDrawFrame();
}
public void handleBeginFrame(TimeSpan? rawTimeStamp) {
Timeline.startSync("Frame");
_firstRawTimeStampInEpoch = _firstRawTimeStampInEpoch ?? rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
if (rawTimeStamp != null)
_lastRawTimeStamp = rawTimeStamp.Value;
D.assert(() => {
_debugFrameNumber += 1;
if (scheduler_.debugPrintBeginFrameBanner || scheduler_.debugPrintEndFrameBanner) {
StringBuilder frameTimeStampDescription = new StringBuilder();
if (rawTimeStamp != null) {
_debugDescribeTimeStamp(_currentFrameTimeStamp.Value, frameTimeStampDescription);
}
else {
frameTimeStampDescription.Append("(warm-up frame)");
}
_debugBanner =
$"▄▄▄▄▄▄▄▄ Frame {_debugFrameNumber.ToString().PadRight(7)} ${frameTimeStampDescription.ToString().PadLeft(18)} ▄▄▄▄▄▄▄▄";
if (scheduler_.debugPrintBeginFrameBanner)
Debug.Log(_debugBanner);
}
return true;
});
D.assert(_schedulerPhase == SchedulerPhase.idle);
_hasScheduledFrame = false;
try {
Timeline.startSync("Animate");
_schedulerPhase = SchedulerPhase.transientCallbacks;
var callbacks = _transientCallbacks;
_transientCallbacks = new Dictionary<int, _FrameCallbackEntry>();
foreach (var entry in callbacks) {
if (!_removedIds.Contains(entry.Key)) {
_invokeFrameCallback(
entry.Value.callback, _currentFrameTimeStamp.Value, entry.Value.debugStack);
}
}
_removedIds.Clear();
}
finally {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}
public void handleDrawFrame() {
D.assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync();
try {
_schedulerPhase = SchedulerPhase.persistentCallbacks;
foreach (FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp.Value);
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
var localPostFrameCallbacks = new List<FrameCallback>(_postFrameCallbacks);
_postFrameCallbacks.Clear();
foreach (FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp.Value);
}
finally {
_schedulerPhase = SchedulerPhase.idle;
D.assert(() => {
if (scheduler_.debugPrintEndFrameBanner)
Debug.Log(new string('▀', _debugBanner.Length));
_debugBanner = null;
return true;
});
_currentFrameTimeStamp = null;
}
}
void _profileFramePostEvent(int frameNumber, FrameTiming frameTiming) {
developer_.postEvent("Flutter.Frame", new Hashtable {
{"number", frameNumber},
{"startTime", frameTiming.timestampInMicroseconds(FramePhase.buildStart)},
{"elapsed", (int) (frameTiming.totalSpan.TotalMilliseconds * 1000)},
{"build", (int) (frameTiming.buildDuration.TotalMilliseconds * 1000)},
{"raster", (int) (frameTiming.rasterDuration.TotalMilliseconds * 1000)},
});
}
static void _debugDescribeTimeStamp(TimeSpan timeStamp, StringBuilder buffer) {
if (timeStamp.TotalDays > 0)
buffer.AppendFormat("{0}d ", timeStamp.Days);
if (timeStamp.TotalHours > 0)
buffer.AppendFormat("{0}h ", timeStamp.Hours);
if (timeStamp.TotalMinutes > 0)
buffer.AppendFormat("{0}m ", timeStamp.Minutes);
if (timeStamp.TotalSeconds > 0)
buffer.AppendFormat("{0}s ", timeStamp.Seconds);
buffer.AppendFormat("{0}", timeStamp.Milliseconds);
int microseconds = (int) (timeStamp.Ticks % 10000 / 10);
if (microseconds > 0)
buffer.AppendFormat(".{0}", microseconds.ToString().PadLeft(3, '0'));
buffer.Append("ms");
}
void _invokeFrameCallback(FrameCallback callback, TimeSpan timeStamp, string callbackStack = null) {
D.assert(callback != null);
D.assert(_FrameCallbackEntry.debugCurrentCallbackStack == null);
D.assert(() => {
_FrameCallbackEntry.debugCurrentCallbackStack = callbackStack;
return true;
});
try {
callback(timeStamp);
}
catch (Exception ex) {
UIWidgetsError.reportError(new UIWidgetsErrorDetails(
exception: ex,
library: "scheduler library",
context: "during a scheduler callback",
informationCollector: callbackStack == null
? (InformationCollector) null
: information => {
information.AppendLine(
"\nThis exception was thrown in the context of a scheduler callback. " +
"When the scheduler callback was _registered_ (as opposed to when the " +
"exception was thrown), this was the stack:"
);
foreach (var line in UIWidgetsError.defaultStackFilter(
callbackStack.TrimEnd().Split('\n'))) {
information.AppendLine(line);
}
}
));
}
D.assert(() => {
_FrameCallbackEntry.debugCurrentCallbackStack = null;
return true;
});
}
}
public static partial class scheduler_ {
public static bool defaultSchedulingStrategy(int priority, SchedulerBinding scheduler) {
if (scheduler.transientCallbackCount > 0)
return priority >= Priority.animation.value;
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0756240ed259436a82cdc6ee65831a4f
timeCreated: 1599458325

Просмотреть файл

@ -0,0 +1,7 @@
namespace Unity.UIWidgets.scheduler2 {
public static partial class scheduler_ {
public static bool debugPrintBeginFrameBanner = false;
public static bool debugPrintEndFrameBanner = false;
public static bool debugPrintScheduleFrameStacks = false;
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 486e9bc3d81445fba548c7cddf682b4d
timeCreated: 1599458325

Просмотреть файл

@ -0,0 +1,30 @@
using UnityEngine;
namespace Unity.UIWidgets.scheduler2 {
public class Priority {
Priority(int value) {
_value = value;
}
public int value => _value;
int _value;
public static readonly Priority idle = new Priority(0);
public static readonly Priority animation = new Priority(100000);
public static readonly Priority touch = new Priority(200000);
public static readonly int kMaxOffset = 10000;
public static Priority operator +(Priority it, int offset) {
if (Mathf.Abs(offset) > kMaxOffset) {
offset = kMaxOffset * (int) Mathf.Sign(offset);
}
return new Priority(it._value + offset);
}
public static Priority operator -(Priority it, int offset) => it + (-offset);
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 814da0eaecc042059b36143e73f60f48
timeCreated: 1599458325

Просмотреть файл

@ -0,0 +1,307 @@
using System;
using System.Text;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace Unity.UIWidgets.scheduler2 {
public delegate void TickerCallback(TimeSpan elapsed);
public interface TickerProvider {
Ticker createTicker(TickerCallback onTick);
}
public class Ticker : IDisposable {
public Ticker(TickerCallback onTick, string debugLabel = null) {
D.assert(() => {
_debugCreationStack = StackTraceUtility.ExtractStackTrace();
return true;
});
_onTick = onTick;
this.debugLabel = debugLabel;
}
TickerFuture _future;
public bool muted {
get => _muted;
set {
if (value == _muted)
return;
_muted = value;
if (value) {
unscheduleTick();
}
else if (shouldScheduleTick) {
scheduleTick();
}
}
}
bool _muted = false;
public bool isTicking {
get {
if (_future == null)
return false;
if (muted)
return false;
if (SchedulerBinding.instance.framesEnabled)
return true;
if (SchedulerBinding.instance.schedulerPhase != SchedulerPhase.idle)
return true;
return false;
}
}
public bool isActive {
get { return _future != null; }
}
TimeSpan? _startTime;
public TickerFuture start() {
D.assert(() => {
if (isActive) {
throw new UIWidgetsError(
"A ticker was started twice.\nA ticker that is already active cannot be started again without first stopping it.\n" +
"The affected ticker was: " + toString(debugIncludeStack: true));
}
return true;
});
D.assert(_startTime == null);
_future = new TickerFuture();
if (shouldScheduleTick) {
scheduleTick();
}
if (SchedulerBinding.instance.schedulerPhase > SchedulerPhase.idle &&
SchedulerBinding.instance.schedulerPhase < SchedulerPhase.postFrameCallbacks) {
_startTime = SchedulerBinding.instance.currentFrameTimeStamp;
}
return _future;
}
public void stop(bool canceled = false) {
if (!isActive)
return;
var localFuture = _future;
_future = null;
_startTime = null;
D.assert(!isActive);
unscheduleTick();
if (canceled) {
localFuture._cancel(this);
}
else {
localFuture._complete();
}
}
readonly TickerCallback _onTick;
int? _animationId;
protected bool scheduled {
get => _animationId != null;
}
protected bool shouldScheduleTick => !muted && isActive && !scheduled;
void _tick(TimeSpan timeStamp) {
D.assert(isTicking);
D.assert(scheduled);
_animationId = null;
_startTime = _startTime ?? timeStamp;
_onTick(timeStamp - _startTime.Value);
if (shouldScheduleTick)
scheduleTick(rescheduling: true);
}
protected void scheduleTick(bool rescheduling = false) {
D.assert(!scheduled);
D.assert(shouldScheduleTick);
_animationId = SchedulerBinding.instance.scheduleFrameCallback(_tick, rescheduling: rescheduling);
}
protected void unscheduleTick() {
if (scheduled) {
SchedulerBinding.instance.cancelFrameCallbackWithId(_animationId.Value);
_animationId = null;
}
D.assert(!shouldScheduleTick);
}
public void absorbTicker(Ticker originalTicker) {
D.assert(!isActive);
D.assert(_future == null);
D.assert(_startTime == null);
D.assert(_animationId == null);
D.assert((originalTicker._future == null) == (originalTicker._startTime == null),
() => "Cannot absorb Ticker after it has been disposed.");
if (originalTicker._future != null) {
_future = originalTicker._future;
_startTime = originalTicker._startTime;
if (shouldScheduleTick) {
scheduleTick();
}
originalTicker._future = null;
originalTicker.unscheduleTick();
}
originalTicker.Dispose();
}
public virtual void Dispose() {
if (_future != null) {
var localFuture = _future;
_future = null;
D.assert(!isActive);
unscheduleTick();
localFuture._cancel(this);
}
D.assert(() => {
_startTime = TimeSpan.Zero;
return true;
});
}
public readonly string debugLabel;
internal string _debugCreationStack;
public override string ToString() {
return toString(debugIncludeStack: false);
}
public string toString(bool debugIncludeStack = false) {
var buffer = new StringBuilder();
buffer.Append(GetType() + "(");
D.assert(() => {
buffer.Append(debugLabel ?? "");
return true;
});
buffer.Append(')');
D.assert(() => {
if (debugIncludeStack) {
buffer.AppendLine();
buffer.AppendLine("The stack trace when the " + GetType() + " was actually created was: ");
foreach (var line in UIWidgetsError.defaultStackFilter(
_debugCreationStack.TrimEnd().Split('\n'))) {
buffer.AppendLine(line);
}
}
return true;
});
return buffer.ToString();
}
}
public class TickerFuture : Future {
internal TickerFuture() {
}
public static TickerFuture complete() {
var result = new TickerFuture();
result._complete();
return result;
}
readonly Completer _primaryCompleter = Completer.create();
Completer _secondaryCompleter;
bool? _completed; // null means unresolved, true means complete, false means canceled
internal void _complete() {
D.assert(_completed == null);
_completed = true;
_primaryCompleter.complete();
_secondaryCompleter?.complete();
}
internal void _cancel(Ticker ticker) {
D.assert(_completed == null);
_completed = false;
_secondaryCompleter?.completeError(new TickerCanceled(ticker));
}
public void whenCompleteOrCancel(VoidCallback callback) {
orCancel.then((value) => {
callback();
return FutureOr.nullValue;
}, ex => {
callback();
return FutureOr.nullValue;
});
}
public Future orCancel {
get {
if (_secondaryCompleter == null) {
_secondaryCompleter = Completer.create();
if (_completed != null) {
if (_completed.Value) {
_secondaryCompleter.complete();
}
else {
_secondaryCompleter.completeError(new TickerCanceled());
}
}
}
return _secondaryCompleter.future;
}
}
// public override Stream asStream() {
// return _primaryCompleter.future.asStream();
// }
public override Future catchError(Func<Exception, FutureOr> onError, Func<Exception, bool> test = null) {
return _primaryCompleter.future.catchError(onError, test: test);
}
public override Future then(Func<object, FutureOr> onValue, Func<Exception, FutureOr> onError = null) {
return _primaryCompleter.future.then(onValue, onError: onError);
}
public override Future timeout(TimeSpan timeLimit, Func<FutureOr> onTimeout = null) {
return _primaryCompleter.future.timeout(timeLimit, onTimeout: onTimeout);
}
public override Future whenComplete(Func<object> action) {
return _primaryCompleter.future.whenComplete(action);
}
public override string ToString() =>
$"{Diagnostics.describeIdentity(this)}({(_completed == null ? "active" : (_completed.Value ? "complete" : "canceled"))})";
}
public class TickerCanceled : Exception {
public TickerCanceled(Ticker ticker = null) {
this.ticker = ticker;
}
public readonly Ticker ticker;
public override string ToString() {
if (ticker != null) {
return "This ticker was canceled: " + ticker;
}
return "The ticker was canceled before the \"orCancel\" property was first used.";
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9ee135f21ca64943a5f75a04a0e30cc3
timeCreated: 1599458326

Просмотреть файл

@ -86,6 +86,7 @@ namespace Unity.UIWidgets.ui2 {
public class Window {
internal IntPtr _ptr;
internal object _binding;
internal Window() {
_setNeedsReportTimings = Window_setNeedsReportTimings;

Просмотреть файл

@ -80,4 +80,22 @@ fml::WeakPtr<ImageDecoder> UIMonoState::GetImageDecoder() const {
return image_decoder_;
}
UIWIDGETS_API(void)
UIMonoState_scheduleMicrotask(MonoMicrotaskQueue::CallbackFunc callback,
Mono_Handle handle) {
UIMonoState::Current()->ScheduleMicrotask(callback, handle);
}
UIWIDGETS_API(void)
UIMonoState_postTaskForTime(MonoMicrotaskQueue::CallbackFunc callback,
Mono_Handle handle, int64_t target_time_nanos) {
UIMonoState::Current()->GetTaskRunners().GetUITaskRunner()->PostTaskForTime(
[callback, handle]() -> void { callback(handle); },
fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(target_time_nanos)));
}
UIWIDGETS_API(int)
UIMonoState_timerMillisecondClock() { return Mono_TimelineGetMicros() / 1000; }
} // namespace uiwidgets

Просмотреть файл

@ -21,6 +21,8 @@ void UIWidgetsSystem::UnregisterPanel(UIWidgetsPanel* panel) {
}
void UIWidgetsSystem::Wait(std::chrono::nanoseconds max_duration) {
Update();
std::chrono::nanoseconds wait_duration =
std::max(std::chrono::nanoseconds(0),
next_uiwidgets_event_time_ - TimePoint::clock::now());
@ -48,8 +50,6 @@ void UIWidgetsSystem::VSync() {
for (auto* uiwidgets_panel : uiwidgets_panels_) {
uiwidgets_panel->ProcessVSync();
}
Update();
}
void UIWidgetsSystem::WakeUp() {}

Просмотреть файл

@ -67,7 +67,7 @@ class Win32TaskRunner {
TaskObservers task_observers_;
static TaskTimePoint TimePointFromUIWidgetsTime(
uint64_t flutter_target_time_nanos);
uint64_t uiwidgets_target_time_nanos);
};
} // namespace uiwidgets