Skip to content

Commit

Permalink
Merge pull request #263 from timcassell/allsettled
Browse files Browse the repository at this point in the history
Add `AllSettled` and `MergeSettled` APIs
  • Loading branch information
timcassell authored Sep 11, 2023
2 parents 9779f6a + c41f688 commit 3a973a3
Show file tree
Hide file tree
Showing 13 changed files with 6,130 additions and 356 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/unity-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
}
# Editor uses 2018.4 to test Net3.5 and Net4.x.
# Standalone uses 2019.4 and 2021.3 to test IL2CPP with netstandard 2.0 and netstandard2.1.
unityVersion: [2018.4.36f1, 2019.4.40f1, 2021.3.15f1]
unityVersion: [2018.4.36f1, 2019.4.40f1, 2021.3.29f1]
devMode:
- {
name: devMode,
Expand All @@ -95,7 +95,7 @@ jobs:
}
- {
testMode: { name: Editor },
unityVersion: 2021.3.15f1
unityVersion: 2021.3.29f1
}
# Standalone with IL2CPP can only be built with 2019.4+ (unity-builder docker images constraint), which doesn't support Net3.5.
- {
Expand Down
423 changes: 102 additions & 321 deletions Package/Core/Promises/Internal/MergeInternal.cs

Large diffs are not rendered by default.

55 changes: 40 additions & 15 deletions Package/Core/Promises/Internal/Progress/ProgressMergeInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,33 +46,57 @@ void IMultiHandleablePromise.ReturnPassthroughs(ValueLinkedStack<PromisePassThro

partial class MergePromise<TResult>
{
internal sealed override PromiseRefBase AddProgressWaiter(short promiseId, out HandleablePromiseBase previousWaiter, ref ProgressHookupValues progressHookupValues)
protected void SetProgressValuesAndGetPrevious(ref ProgressHookupValues progressHookupValues, bool reportUnresolved)
{
ThrowIfInPool(this);
// Retain this until we've hooked up progress to the passthroughs. This is necessary because we take the passthroughs, then put back the ones that are unable to be hooked up.
InterlockedAddWithUnsignedOverflowCheck(ref _retainCounter, 1);
progressHookupValues.RegisterHandler(this);
ProgressMerger.MaybeHookup(this, _completeProgress, _passThroughs.TakeAndClear(), ref progressHookupValues, reportUnresolved);
}

internal override PromiseRefBase AddProgressWaiter(short promiseId, out HandleablePromiseBase previousWaiter, ref ProgressHookupValues progressHookupValues)
{
var promiseSingleAwait = AddWaiterImpl(promiseId, progressHookupValues.ProgressListener, out previousWaiter);
if (previousWaiter == PendingAwaitSentinel.s_instance)
{
SetProgressValuesAndGetPrevious(ref progressHookupValues);
SetProgressValuesAndGetPrevious(ref progressHookupValues, false);
}
return promiseSingleAwait;
}

new private void SetProgressValuesAndGetPrevious(ref ProgressHookupValues progressHookupValues)
internal override bool TryHookupProgressListenerAndGetPrevious(ref ProgressHookupValues progressHookupValues)
{
ThrowIfInPool(this);
// Retain this until we've hooked up progress to the passthroughs. This is necessary because we take the passthroughs, then put back the ones that are unable to be hooked up.
InterlockedAddWithUnsignedOverflowCheck(ref _retainCounter, 1);
progressHookupValues.RegisterHandler(this);
ProgressMerger.MaybeHookup(this, _completeProgress, _passThroughs.TakeAndClear(), ref progressHookupValues);
if (CompareExchangeWaiter(progressHookupValues.ProgressListener, progressHookupValues._expectedWaiter) != progressHookupValues._expectedWaiter)
{
progressHookupValues._previous = null;
return false;
}
SetProgressValuesAndGetPrevious(ref progressHookupValues, false);
return true;
}
}

partial class MergeSettledPromise<TResult>
{
internal override PromiseRefBase AddProgressWaiter(short promiseId, out HandleablePromiseBase previousWaiter, ref ProgressHookupValues progressHookupValues)
{
var promiseSingleAwait = AddWaiterImpl(promiseId, progressHookupValues.ProgressListener, out previousWaiter);
if (previousWaiter == PendingAwaitSentinel.s_instance)
{
SetProgressValuesAndGetPrevious(ref progressHookupValues, true);
}
return promiseSingleAwait;
}

internal override sealed bool TryHookupProgressListenerAndGetPrevious(ref ProgressHookupValues progressHookupValues)
internal override bool TryHookupProgressListenerAndGetPrevious(ref ProgressHookupValues progressHookupValues)
{
if (CompareExchangeWaiter(progressHookupValues.ProgressListener, progressHookupValues._expectedWaiter) != progressHookupValues._expectedWaiter)
{
progressHookupValues._previous = null;
return false;
}
SetProgressValuesAndGetPrevious(ref progressHookupValues);
SetProgressValuesAndGetPrevious(ref progressHookupValues, true);
return true;
}
}
Expand Down Expand Up @@ -115,14 +139,15 @@ private static ProgressMerger GetOrCreate()
: obj.UnsafeAs<ProgressMerger>();
}

private static ProgressMerger GetOrCreate(PromiseRefBase targetMergePromise, ulong completedProgress, ulong expectedProgress, ValueLinkedStack<PromisePassThrough> passThroughs)
private static ProgressMerger GetOrCreate(PromiseRefBase targetMergePromise, ulong completedProgress, ulong expectedProgress, ValueLinkedStack<PromisePassThrough> passThroughs, bool reportUnresolved)
{
var merger = GetOrCreate();
merger._next = null;
merger._targetMergePromise = targetMergePromise;
merger._passThroughs = passThroughs;
merger._currentProgress = completedProgress;
merger._divisorReciprocal = 1d / expectedProgress;
merger._reportUnresolved = reportUnresolved;
#if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE
merger._disposed = false;
#endif
Expand All @@ -138,7 +163,7 @@ private void Dispose()
ObjectPool.MaybeRepool(this);
}

internal static void MaybeHookup(PromiseRefBase targetMergePromise, ulong completedProgress, ValueLinkedStack<PromisePassThrough> passThroughs, ref ProgressHookupValues progressHookupValues)
internal static void MaybeHookup(PromiseRefBase targetMergePromise, ulong completedProgress, ValueLinkedStack<PromisePassThrough> passThroughs, ref ProgressHookupValues progressHookupValues, bool reportUnresolved)
{
progressHookupValues._previous = null;
ushort depth = targetMergePromise.Depth;
Expand All @@ -155,7 +180,7 @@ internal static void MaybeHookup(PromiseRefBase targetMergePromise, ulong comple
{
expectedProgress += pt.Depth + 1u;
}
var merger = GetOrCreate(targetMergePromise, completedProgress, expectedProgress, passThroughs);
var merger = GetOrCreate(targetMergePromise, completedProgress, expectedProgress, passThroughs, reportUnresolved);
progressHookupValues._currentProgress = completedProgress * merger._divisorReciprocal;

progressHookupValues.AddPassthrough(merger);
Expand Down Expand Up @@ -236,10 +261,10 @@ internal void Handle(float oldProgress, float maxProgress, PromiseRefBase handle
var progressReportValues = new ProgressReportValues(null, this, lockedObject, maxProgress);
UpdateProgress(oldProgress, ref progressReportValues);

// We only report the progress if the handler was not the last completed, and the state is resolved.
// We only report the progress if the handler was not the last completed, and the state is resolved (or the merge type is All/MergeSettled).
// We check the more common case first.
bool isComplete = InterlockedAddWithUnsignedOverflowCheck(ref _retainCounter, -1) == 0;
if (!isComplete & state == Promise.State.Resolved)
if (!isComplete & (_reportUnresolved | state == Promise.State.Resolved))
{
progressReportValues.ReportProgressToAllListeners();
}
Expand Down
1 change: 1 addition & 0 deletions Package/Core/Promises/Internal/PromiseFieldsInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ partial class ProgressMerger : ProgressPassThrough
private double _currentProgress;
private double _divisorReciprocal; // 1 / expectedProgress since multiplying is faster than dividing.
volatile private int _retainCounter;
private bool _reportUnresolved;
// The passthroughs are only stored during the hookup.
private ValueLinkedStack<PromisePassThrough> _passThroughs;
#if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE
Expand Down
Loading

0 comments on commit 3a973a3

Please sign in to comment.