Skip to content

Commit e6c3caf

Browse files
committed
#46: fix deadlock in RosterTree
1 parent 1d9cc60 commit e6c3caf

File tree

3 files changed

+90
-20
lines changed

3 files changed

+90
-20
lines changed

src/JabberNet.Muzzle/ControlExtensions.cs

+33
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Linq;
3+
using System.Threading.Tasks;
34
using System.Windows.Forms;
45

56
namespace JabberNet.Muzzle
@@ -23,6 +24,38 @@ public static void InvokeAction(this Control control, Action action)
2324
});
2425
}
2526

27+
/// <summary>
28+
/// Asynchronously invokes the action in the control owning thread if invocation is required.
29+
/// </summary>
30+
/// <param name="control">Control on which the action should be invoked.</param>
31+
/// <param name="action">The invocable action.</param>
32+
public static Task BeginInvokeAction(this Control control, Action action)
33+
{
34+
var source = new TaskCompletionSource<object>();
35+
if (control.InvokeRequired)
36+
{
37+
control.BeginInvoke((Action)(() =>
38+
{
39+
try
40+
{
41+
action();
42+
source.SetResult(null);
43+
}
44+
catch (Exception exception)
45+
{
46+
source.SetException(exception);
47+
}
48+
}));
49+
}
50+
else
51+
{
52+
action();
53+
source.SetResult(null);
54+
}
55+
56+
return source.Task;
57+
}
58+
2659
/// <summary>
2760
/// Invoke the function in the control owning thread if invocation is required.
2861
/// </summary>

src/JabberNet.Muzzle/RosterTree.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ private void m_client_OnDisconnect(object sender)
510510

511511
private void m_pres_OnPrimarySessionChange(object sender, JID bare)
512512
{
513-
this.InvokeAction(() =>
513+
this.BeginInvokeAction(() =>
514514
{
515515
var pres = m_pres[bare];
516516
var nodelist = (LinkedList)m_items[bare.ToString()];

tests/JabberNet.Test/Muzzle/ControlExtensionTests.cs

+56-19
Original file line numberDiff line numberDiff line change
@@ -50,33 +50,39 @@ public void InvocationShouldBePerformedInAnotherThreadInCaseOfMultiThreading()
5050
var form = new Form();
5151
var handle = form.Handle;
5252

53-
Exception exception = null;
54-
Task.Factory.StartNew(() =>
55-
{
56-
try
53+
RunApplicationWithParallelTask(
54+
form,
55+
() =>
5756
{
5857
Assert.IsTrue(form.InvokeRequired);
5958
form.InvokeAction(() =>
6059
{
6160
Assert.AreEqual(threadId, Thread.CurrentThread.ManagedThreadId);
6261
});
63-
}
64-
catch (Exception ex)
65-
{
66-
exception = ex;
67-
}
68-
finally
69-
{
70-
Application.Exit();
71-
}
72-
});
62+
});
63+
});
64+
}
7365

74-
Application.Run(form);
75-
if (exception != null)
76-
{
77-
throw exception;
78-
}
66+
[Test]
67+
public void BeginInvokeShouldReturnFinishedTask()
68+
{
69+
Task result = null;
70+
ExecuteInStaThread(() =>
71+
{
72+
var form = new Form();
73+
var handle = form.Handle;
74+
75+
RunApplicationWithParallelTask(
76+
form,
77+
() =>
78+
{
79+
Assert.IsTrue(form.InvokeRequired);
80+
result = form.BeginInvokeAction(Application.Exit);
81+
});
7982
});
83+
84+
Assert.IsNotNull(result);
85+
Assert.IsTrue(result.IsCompleted);
8086
}
8187

8288
[Test]
@@ -121,5 +127,36 @@ private static void ExecuteInStaThread(Action action)
121127
throw exception;
122128
}
123129
}
130+
131+
private static void ExecuteInTask(Action action, Action<Exception> catchClause, Action finallyClause)
132+
{
133+
Task.Factory.StartNew(() =>
134+
{
135+
try
136+
{
137+
action();
138+
}
139+
catch (Exception exception)
140+
{
141+
catchClause(exception);
142+
}
143+
finally
144+
{
145+
finallyClause();
146+
}
147+
});
148+
}
149+
150+
private static void RunApplicationWithParallelTask(Form mainForm, Action task)
151+
{
152+
Exception exception = null;
153+
ExecuteInTask(task, ex => exception = ex, Application.Exit);
154+
155+
Application.Run(mainForm);
156+
if (exception != null)
157+
{
158+
throw exception;
159+
}
160+
}
124161
}
125162
}

0 commit comments

Comments
 (0)