Kevin Gu
4 年前
当前提交
b00d53eb
共有 42 个文件被更改,包括 3655 次插入 和 37 次删除
-
7Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery/app.cs
-
18com.unity.uiwidgets/Runtime/async/priority_queue.cs
-
2com.unity.uiwidgets/Runtime/debugger/inspector_service.cs
-
1com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanel.cs
-
55com.unity.uiwidgets/Runtime/foundation/binding.cs
-
20com.unity.uiwidgets/Runtime/foundation/constants.cs
-
20com.unity.uiwidgets/Runtime/foundation/debug.cs
-
4com.unity.uiwidgets/Runtime/material/slider.cs
-
5com.unity.uiwidgets/Runtime/painting/image_stream.cs
-
12com.unity.uiwidgets/Runtime/scheduler/binding.cs
-
1com.unity.uiwidgets/Runtime/ui2/window.cs
-
18engine/src/lib/ui/ui_mono_state.cc
-
4engine/src/shell/platform/unity/uiwidgets_system.cc
-
2engine/src/shell/platform/unity/win32_task_runner.h
-
3com.unity.uiwidgets/Runtime/async2.meta
-
3com.unity.uiwidgets/Runtime/developer.meta
-
3com.unity.uiwidgets/Runtime/scheduler2.meta
-
360com.unity.uiwidgets/Runtime/async2/future.cs
-
3com.unity.uiwidgets/Runtime/async2/future.cs.meta
-
812com.unity.uiwidgets/Runtime/async2/future_impl.cs
-
3com.unity.uiwidgets/Runtime/async2/future_impl.cs.meta
-
129com.unity.uiwidgets/Runtime/async2/schedule_microtask.cs
-
3com.unity.uiwidgets/Runtime/async2/schedule_microtask.cs.meta
-
142com.unity.uiwidgets/Runtime/async2/timer.cs
-
3com.unity.uiwidgets/Runtime/async2/timer.cs.meta
-
1001com.unity.uiwidgets/Runtime/async2/zone.cs
-
3com.unity.uiwidgets/Runtime/async2/zone.cs.meta
-
8com.unity.uiwidgets/Runtime/developer/extension.cs
-
3com.unity.uiwidgets/Runtime/developer/extension.cs.meta
-
11com.unity.uiwidgets/Runtime/developer/timeline.cs
-
3com.unity.uiwidgets/Runtime/developer/timeline.cs.meta
-
674com.unity.uiwidgets/Runtime/scheduler2/binding.cs
-
3com.unity.uiwidgets/Runtime/scheduler2/binding.cs.meta
-
7com.unity.uiwidgets/Runtime/scheduler2/debug.cs
-
3com.unity.uiwidgets/Runtime/scheduler2/debug.cs.meta
-
30com.unity.uiwidgets/Runtime/scheduler2/priority.cs
-
3com.unity.uiwidgets/Runtime/scheduler2/priority.cs.meta
-
307com.unity.uiwidgets/Runtime/scheduler2/ticker.cs
-
3com.unity.uiwidgets/Runtime/scheduler2/ticker.cs.meta
|
|||
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); |
|||
} |
|||
} |
|||
} |
|
|||
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
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0dd4bb15542f4ad9a7bef43d7defab5f |
|||
timeCreated: 1599458064 |
|
|||
fileFormatVersion: 2 |
|||
guid: 32b343b53c4d484f8c149742f9d0a919 |
|||
timeCreated: 1599028965 |
|
|||
fileFormatVersion: 2 |
|||
guid: 1fea1ea4786b4e6bb65b233b11e8b2d5 |
|||
timeCreated: 1599458322 |
|
|||
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."); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bdf10194e91e4558bc97ebad8b81d5a0 |
|||
timeCreated: 1599458114 |
|
|||
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); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 4072406cc46448f99159c137b458841e |
|||
timeCreated: 1599458114 |
|
|||
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); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e9d7bdadaa9c46e0b96ba19f4261185a |
|||
timeCreated: 1599458114 |
|
|||
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); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: dc44ad1967714937b709d5f22c6b3bec |
|||
timeCreated: 1599458114 |
1001
com.unity.uiwidgets/Runtime/async2/zone.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: d8dbf866190b44e6a47e0ff7c0e2bdc4 |
|||
timeCreated: 1599458114 |
|
|||
using System.Collections; |
|||
|
|||
namespace developer { |
|||
public static partial class developer_ { |
|||
public static void postEvent(string eventKind, Hashtable eventData) { |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d2dcff701560471ab813d010a98a7d36 |
|||
timeCreated: 1599030815 |
|
|||
using System.Collections; |
|||
|
|||
namespace developer { |
|||
public class Timeline { |
|||
public static void startSync(string name, Hashtable arguments = null) { |
|||
} |
|||
|
|||
public static void finishSync() { |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 263dee475b944a33a99e0d8925f904c4 |
|||
timeCreated: 1599028971 |
|
|||
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; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0756240ed259436a82cdc6ee65831a4f |
|||
timeCreated: 1599458325 |
|
|||
namespace Unity.UIWidgets.scheduler2 { |
|||
public static partial class scheduler_ { |
|||
public static bool debugPrintBeginFrameBanner = false; |
|||
public static bool debugPrintEndFrameBanner = false; |
|||
public static bool debugPrintScheduleFrameStacks = false; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 486e9bc3d81445fba548c7cddf682b4d |
|||
timeCreated: 1599458325 |
|
|||
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); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 814da0eaecc042059b36143e73f60f48 |
|||
timeCreated: 1599458325 |
|
|||
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."; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9ee135f21ca64943a5f75a04a0e30cc3 |
|||
timeCreated: 1599458326 |
撰写
预览
正在加载...
取消
保存
Reference in new issue