Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 471b6cb

Browse files
committed
Fix race condition when finishing ui flows
Let's decide that the `IUIController.ListenToCompletionState` observable finishes first, and then the `IUIController.SelectFlow` observable. Once the `SelectFlow` observable is completed, it's a fair bet that the `IUIController` is disposed and it isn't safe to access it for anything else. Fixes #203 properly
1 parent 1304b27 commit 471b6cb

File tree

5 files changed

+34
-21
lines changed

5 files changed

+34
-21
lines changed

src/GitHub.App/Controllers/UIController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ public IObservable<bool> ListenToCompletionState()
153153
void End(bool success)
154154
{
155155
uiProvider.RemoveService(typeof(IConnection));
156-
transition.OnCompleted();
157156
completion?.OnNext(success);
158157
completion?.OnCompleted();
158+
transition.OnCompleted();
159159
completion = null;
160160
}
161161

src/GitHub.Exports/Services/IUIProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ public interface IUIProvider : IServiceProvider
2424
IObservable<UserControl> SetupUI(UIControllerFlow controllerFlow, IConnection connection);
2525
void RunUI();
2626
void RunUI(UIControllerFlow controllerFlow, IConnection connection);
27+
IObservable<bool> ListenToCompletionState();
2728
}
2829
}

src/GitHub.VisualStudio/Services/UIProvider.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class UIProvider : IUIProvider, IDisposable
3131
CompositionContainer tempContainer;
3232
readonly Dictionary<string, ComposablePart> tempParts;
3333
ExportLifetimeContext<IUIController> currentUIFlow;
34+
IUIController currentUI;
3435
readonly Version currentVersion;
3536
bool initializingLogging = false;
3637

@@ -201,9 +202,9 @@ public IObservable<UserControl> SetupUI(UIControllerFlow controllerFlow, [AllowN
201202

202203
var factory = GetService<IExportFactoryProvider>();
203204
currentUIFlow = factory.UIControllerFactory.CreateExport();
205+
currentUI = currentUIFlow.Value;
204206
var disposable = currentUIFlow;
205-
var ui = currentUIFlow.Value;
206-
var creation = ui.SelectFlow(controllerFlow);
207+
var creation = currentUI.SelectFlow(controllerFlow);
207208
windowController = new UI.WindowController(creation);
208209
windowController.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
209210
windowController.Closed += StopUIFlowWhenWindowIsClosedByUser;
@@ -216,10 +217,16 @@ public IObservable<UserControl> SetupUI(UIControllerFlow controllerFlow, [AllowN
216217
else
217218
StopUI();
218219
});
219-
ui.Start(connection);
220+
currentUI.Start(connection);
220221
return creation;
221222
}
222223

224+
public IObservable<bool> ListenToCompletionState()
225+
{
226+
Debug.Assert(currentUI != null, "Cannot call ListenToCompletionState without calling SetupUI first");
227+
return currentUI?.ListenToCompletionState();
228+
}
229+
223230
public void RunUI()
224231
{
225232
if (!Initialized)
@@ -272,6 +279,7 @@ public void StopUI()
272279
}
273280

274281
StopUI(currentUIFlow);
282+
currentUI = null;
275283
currentUIFlow = null;
276284
}
277285

src/GitHub.VisualStudio/TeamExplorer/Connect/GitHubConnectSection.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,18 @@ void StartFlow(UIControllerFlow controllerFlow)
323323
{
324324
var uiProvider = ServiceProvider.GetExportedValue<IUIProvider>();
325325
uiProvider.GitServiceProvider = ServiceProvider;
326-
var ret = uiProvider.SetupUI(controllerFlow, SectionConnection);
327-
ret.Subscribe(_ => {}, () =>
328-
{
329-
if (controllerFlow == UIControllerFlow.Clone)
330-
isCloning = true;
331-
else if (controllerFlow == UIControllerFlow.Create)
332-
isCreating = true;
333-
});
326+
uiProvider.SetupUI(controllerFlow, SectionConnection);
327+
uiProvider.ListenToCompletionState()
328+
.Subscribe(success =>
329+
{
330+
if (success)
331+
{
332+
if (controllerFlow == UIControllerFlow.Clone)
333+
isCloning = true;
334+
else if (controllerFlow == UIControllerFlow.Create)
335+
isCreating = true;
336+
}
337+
});
334338
uiProvider.RunUI();
335339
}
336340

src/GitHub.VisualStudio/TeamExplorer/Sync/GitHubPublishSection.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,20 @@ void ShowPublish()
118118
var ui = uiflow.Value;
119119
var creation = ui.SelectFlow(UIControllerFlow.Publish);
120120
bool success = false;
121-
ui.ListenToCompletionState().Subscribe(s => success = s, () =>
122-
{
123-
// there's no real cancel button in the publish form, but if support a back button there, then we want to hide the form
124-
IsVisible = false;
125-
if (success)
126-
ServiceProvider.TryGetService<ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
127-
});
121+
ui.ListenToCompletionState().Subscribe(s => success = s);
128122

129123
creation.Subscribe(c =>
130124
{
131125
SectionContent = c;
132126
c.DataContext = this;
133127
((IView)c).IsBusy.Subscribe(x => IsBusy = x);
128+
},
129+
() =>
130+
{
131+
// there's no real cancel button in the publish form, but if support a back button there, then we want to hide the form
132+
IsVisible = false;
133+
if (success)
134+
ServiceProvider.TryGetService<ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
134135
});
135136
ui.Start(null);
136137
}
@@ -142,9 +143,8 @@ protected override void Dispose(bool disposing)
142143
{
143144
if (!disposed)
144145
{
145-
if (disposable != null)
146-
disposable.Dispose();
147146
disposed = true;
147+
disposable?.Dispose();
148148
}
149149
}
150150
base.Dispose(disposing);

0 commit comments

Comments
 (0)