Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit 577e243

Browse files
committed
Make sure fault handlers are always called when merging two chains
1 parent 70b9da2 commit 577e243

File tree

3 files changed

+59
-22
lines changed

3 files changed

+59
-22
lines changed

src/GitHub.Api/Tasks/TaskBase.cs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public enum TaskRunOptions
99
{
1010
OnSuccess,
1111
OnFailure,
12-
Always
12+
OnAlways
1313
}
1414

1515
public interface ITask : IAsyncResult
@@ -72,9 +72,9 @@ public abstract class TaskBase : ITask
7272

7373
protected TaskBase continuationOnSuccess;
7474
protected TaskBase continuationOnFailure;
75-
protected TaskBase continuationAlways;
75+
protected TaskBase continuationOnAlways;
7676

77-
protected event Func<Exception, bool> faultHandler;
77+
protected event Func<Exception, bool> catchHandler;
7878
private event Action finallyHandler;
7979
protected event Action<IProgress> progressHandler;
8080

@@ -135,11 +135,29 @@ public virtual T Then<T>(T nextTask, TaskRunOptions runOptions = TaskRunOptions.
135135
firstTaskBase.SetDependsOn(this);
136136

137137
if (runOptions == TaskRunOptions.OnSuccess)
138+
{
138139
this.continuationOnSuccess = firstTaskBase;
140+
// if there are fault handlers in the chain we're appending, propagate them
141+
// up this chain as well
142+
if (firstTaskBase.continuationOnFailure != null)
143+
SetFaultHandler(firstTaskBase.continuationOnFailure);
144+
else if (firstTaskBase.continuationOnAlways != null)
145+
SetFaultHandler(firstTaskBase.continuationOnAlways);
146+
if (firstTaskBase.catchHandler != null)
147+
Catch(firstTaskBase.catchHandler);
148+
if (firstTaskBase.finallyHandler != null)
149+
Finally(firstTaskBase.finallyHandler);
150+
}
139151
else if (runOptions == TaskRunOptions.OnFailure)
152+
{
140153
this.continuationOnFailure = firstTaskBase;
154+
DependsOn?.SetFaultHandler(firstTaskBase);
155+
}
141156
else
142-
this.continuationAlways = firstTaskBase;
157+
{
158+
this.continuationOnAlways = firstTaskBase;
159+
DependsOn?.SetFaultHandler(firstTaskBase);
160+
}
143161
return nextTask;
144162
}
145163

@@ -150,7 +168,7 @@ public virtual T Then<T>(T nextTask, TaskRunOptions runOptions = TaskRunOptions.
150168
public ITask Catch(Action<Exception> handler)
151169
{
152170
Guard.ArgumentNotNull(handler, "handler");
153-
faultHandler += e => { handler(e); return false; };
171+
catchHandler += e => { handler(e); return false; };
154172
DependsOn?.Catch(handler);
155173
return this;
156174
}
@@ -162,7 +180,7 @@ public ITask Catch(Action<Exception> handler)
162180
public ITask Catch(Func<Exception, bool> handler)
163181
{
164182
Guard.ArgumentNotNull(handler, "handler");
165-
faultHandler += handler;
183+
catchHandler += handler;
166184
DependsOn?.Catch(handler);
167185
return this;
168186
}
@@ -188,10 +206,10 @@ public ITask Finally<T>(T taskToContinueWith)
188206
where T : ITask
189207
{
190208
Guard.ArgumentNotNull(taskToContinueWith, nameof(taskToContinueWith));
191-
continuationAlways = (TaskBase)(object)taskToContinueWith;
192-
continuationAlways.SetDependsOn(this);
193-
DependsOn?.SetFaultHandler(continuationAlways);
194-
return continuationAlways;
209+
continuationOnAlways = (TaskBase)(object)taskToContinueWith;
210+
continuationOnAlways.SetDependsOn(this);
211+
DependsOn?.SetFaultHandler(continuationOnAlways);
212+
return continuationOnAlways;
195213
}
196214

197215
internal void SetFaultHandler(TaskBase handler)
@@ -275,12 +293,12 @@ protected virtual void RunContinuation()
275293
TaskManager.GetScheduler(continuationOnFailure.Affinity));
276294
}
277295

278-
if (continuationAlways != null)
296+
if (continuationOnAlways != null)
279297
{
280298
//Logger.Trace($"Setting ContinueWith {Affinity} {continuation}");
281-
Task.ContinueWith(_ => ((TaskBase)(object)continuationAlways).Run(), Token,
299+
Task.ContinueWith(_ => ((TaskBase)(object)continuationOnAlways).Run(), Token,
282300
runAlwaysOptions,
283-
TaskManager.GetScheduler(continuationAlways.Affinity));
301+
TaskManager.GetScheduler(continuationOnAlways.Affinity));
284302
}
285303
}
286304

@@ -338,17 +356,17 @@ protected virtual void RaiseOnStart()
338356
protected virtual void RaiseOnEnd()
339357
{
340358
OnEnd?.Invoke(this);
341-
if (continuationOnSuccess == null && continuationOnFailure == null && continuationAlways == null)
359+
if (continuationOnSuccess == null && continuationOnFailure == null && continuationOnAlways == null)
342360
finallyHandler?.Invoke();
343361
//Logger.Trace($"Finished {ToString()}");
344362
}
345363

346364
protected virtual bool RaiseFaultHandlers(Exception ex)
347365
{
348-
if (faultHandler == null)
366+
if (catchHandler == null)
349367
return false;
350368
bool handled = false;
351-
foreach (var handler in faultHandler.GetInvocationList())
369+
foreach (var handler in catchHandler.GetInvocationList())
352370
{
353371
handled |= (bool)handler.DynamicInvoke(new object[] { ex });
354372
if (handled)
@@ -459,7 +477,7 @@ public override T Then<T>(T continuation, TaskRunOptions runOptions = TaskRunOpt
459477
public new ITask<TResult> Catch(Action<Exception> handler)
460478
{
461479
Guard.ArgumentNotNull(handler, "handler");
462-
faultHandler += e => { handler(e); return false; };
480+
catchHandler += e => { handler(e); return false; };
463481
DependsOn?.Catch(handler);
464482
return this;
465483
}
@@ -472,7 +490,7 @@ public override T Then<T>(T continuation, TaskRunOptions runOptions = TaskRunOpt
472490
public new ITask<TResult> Catch(Func<Exception, bool> handler)
473491
{
474492
Guard.ArgumentNotNull(handler, "handler");
475-
faultHandler += handler;
493+
catchHandler += handler;
476494
DependsOn?.Catch(handler);
477495
return this;
478496
}
@@ -491,15 +509,15 @@ public ITask<TResult> Finally(Action<TResult> handler)
491509
public ITask<TResult> Finally(Func<bool, Exception, TResult, TResult> continuation, TaskAffinity affinity = TaskAffinity.Concurrent)
492510
{
493511
Guard.ArgumentNotNull(continuation, "continuation");
494-
var ret = Then(new FuncTask<TResult, TResult>(Token, continuation) { Affinity = affinity, Name = "Finally" }, TaskRunOptions.Always);
512+
var ret = Then(new FuncTask<TResult, TResult>(Token, continuation) { Affinity = affinity, Name = "Finally" }, TaskRunOptions.OnAlways);
495513
DependsOn?.SetFaultHandler(ret);
496514
return ret;
497515
}
498516

499517
public ITask Finally(Action<bool, Exception, TResult> continuation, TaskAffinity affinity = TaskAffinity.Concurrent)
500518
{
501519
Guard.ArgumentNotNull(continuation, "continuation");
502-
var ret = Then(new ActionTask<TResult>(Token, continuation) { Affinity = affinity, Name = "Finally" }, TaskRunOptions.Always);
520+
var ret = Then(new ActionTask<TResult>(Token, continuation) { Affinity = affinity, Name = "Finally" }, TaskRunOptions.OnAlways);
503521
DependsOn?.SetFaultHandler(ret);
504522
return ret;
505523
}
@@ -542,7 +560,7 @@ protected override void RaiseOnStart()
542560
protected virtual void RaiseOnEnd(TResult result)
543561
{
544562
OnEnd?.Invoke(this, result);
545-
if (continuationOnSuccess == null && continuationOnFailure == null && continuationAlways == null)
563+
if (continuationOnSuccess == null && continuationOnFailure == null && continuationOnAlways == null)
546564
finallyHandler?.Invoke(result);
547565
//Logger.Trace($"Finished {ToString()} {result}");
548566
}

src/UnityExtension/Assets/Editor/GitHub.Unity/UI/HistoryView.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ private void Pull()
677677
// whether pull triggered a merge or a rebase, and abort the operation accordingly
678678
// (either git rebase --abort or git merge --abort)
679679
}
680-
}, TaskRunOptions.Always)
680+
}, TaskRunOptions.OnAlways)
681681
.FinallyInUI((success, e) => {
682682
if (success)
683683
{

src/tests/TaskSystemIntegrationTests/Tests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,25 @@ public async Task StartAwaitSafelyAwaits()
666666
.Catch(_ => { });
667667
await task.StartAwait(_ => { });
668668
}
669+
670+
[Test]
671+
public async Task TaskOnFailureGetsCalledWhenExceptionHappensUpTheChain()
672+
{
673+
var runOrder = new List<string>();
674+
var exceptions = new List<Exception>();
675+
var task = new ActionTask(Token, _ => { throw new InvalidOperationException(); })
676+
.Then(_ => { runOrder.Add("1"); })
677+
.Catch(ex => exceptions.Add(ex))
678+
.Then(() => runOrder.Add("OnFailure"), TaskRunOptions.OnFailure)
679+
.Finally((s, e) => { });
680+
await task.StartAndSwallowException();
681+
CollectionAssert.AreEqual(
682+
new string[] { typeof(InvalidOperationException).Name },
683+
exceptions.Select(x => x.GetType().Name).ToArray());
684+
CollectionAssert.AreEqual(
685+
new string[] { "OnFailure" },
686+
runOrder);
687+
}
669688
}
670689

671690
[TestFixture]

0 commit comments

Comments
 (0)