Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.

Commit 1ded41d

Browse files
committed
Fix race conditions is case of threaded Iterate() call
1 parent b288288 commit 1ded41d

File tree

6 files changed

+142
-111
lines changed

6 files changed

+142
-111
lines changed

src/Bus.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ public static Bus Starter
5959
throw new ArgumentNullException ("address");
6060

6161
Bus bus;
62-
if (buses.TryGetValue (address, out bus))
63-
return bus;
62+
lock (buses) {
63+
if (buses.TryGetValue (address, out bus))
64+
return bus;
6465

65-
bus = new Bus (address);
66-
buses[address] = bus;
66+
bus = new Bus (address);
67+
buses[address] = bus;
68+
}
6769

6870
return bus;
6971
}

src/BusException.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public override string Message
2525
{
2626
get
2727
{
28-
return ErrorName + ": " + ErrorMessage;
28+
return ErrorName + ": " + ErrorMessage + Environment.NewLine + this.StackTrace;
2929
}
3030
}
3131

src/Connection.cs

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic;
77
using System.IO;
88
using System.Threading;
9+
using System.Threading.Tasks;
910
using System.Reflection;
1011

1112
namespace DBus
@@ -42,9 +43,11 @@ public class Connection
4243
public delegate void MonitorEventHandler (Message msg);
4344
public MonitorEventHandler Monitors; // subscribe yourself to this list of observers if you want to get notified about each incoming message
4445

46+
private ManualResetEventSlim iterateEvent = MakeNewEventToNextIterate ();
47+
private readonly object iterateLocker = new object ();
48+
4549
protected Connection ()
4650
{
47-
4851
}
4952

5053
internal Connection (Transport transport)
@@ -183,11 +186,12 @@ internal uint GenerateSerial ()
183186

184187
internal Message SendWithReplyAndBlock (Message msg, bool keepFDs)
185188
{
186-
PendingCall pending = SendWithReply (msg, keepFDs);
187-
return pending.Reply;
189+
using (PendingCall pending = SendWithPendingReply (msg, keepFDs)) {
190+
return pending.Reply;
191+
}
188192
}
189193

190-
internal PendingCall SendWithReply (Message msg, bool keepFDs)
194+
internal PendingCall SendWithPendingReply (Message msg, bool keepFDs)
191195
{
192196
msg.ReplyExpected = true;
193197

@@ -232,10 +236,30 @@ internal void DispatchSignals ()
232236

233237
public void Iterate ()
234238
{
235-
Message msg = transport.ReadMessage ();
239+
Iterate (new CancellationToken (false));
240+
}
241+
242+
Task Task = null;
243+
object TaskLock = new object ();
236244

237-
HandleMessage (msg);
238-
DispatchSignals ();
245+
public void Iterate (CancellationToken stopWaitToken)
246+
{
247+
lock (TaskLock) {
248+
if (Task == null || Task.IsCompleted) {
249+
Task = Task.Run ( () => {
250+
var msg = transport.ReadMessage ();
251+
HandleMessage (msg);
252+
DispatchSignals ();
253+
});
254+
}
255+
}
256+
257+
Task.Wait (stopWaitToken);
258+
}
259+
260+
private static ManualResetEventSlim MakeNewEventToNextIterate ()
261+
{
262+
return new ManualResetEventSlim (true);
239263
}
240264

241265
internal virtual void HandleMessage (Message msg)
@@ -251,21 +275,19 @@ internal virtual void HandleMessage (Message msg)
251275
try {
252276

253277
//TODO: Restrict messages to Local ObjectPath?
254-
255278
{
256-
object field_value = msg.Header[FieldCode.ReplySerial];
279+
object field_value = msg.Header [FieldCode.ReplySerial];
257280
if (field_value != null) {
258281
uint reply_serial = (uint)field_value;
259-
PendingCall pending;
260282

261283
lock (pendingCalls) {
284+
PendingCall pending;
262285
if (pendingCalls.TryGetValue (reply_serial, out pending)) {
263-
if (pendingCalls.Remove (reply_serial)) {
264-
pending.Reply = msg;
265-
if (pending.KeepFDs)
266-
cleanupFDs = false; // caller is responsible for closing FDs
267-
}
268-
286+
if (!pendingCalls.Remove (reply_serial))
287+
return;
288+
pending.Reply = msg;
289+
if (pending.KeepFDs)
290+
cleanupFDs = false; // caller is responsible for closing FDs
269291
return;
270292
}
271293
}
@@ -391,17 +413,19 @@ internal void HandleMethodCall (MessageContainer method_call)
391413
//this is messy and inefficient
392414
List<string> linkNodes = new List<string> ();
393415
int depth = method_call.Path.Decomposed.Length;
394-
foreach (ObjectPath pth in registeredObjects.Keys) {
395-
if (pth.Value == (method_call.Path.Value)) {
396-
ExportObject exo = (ExportObject)registeredObjects[pth];
397-
exo.WriteIntrospect (intro);
398-
} else {
399-
for (ObjectPath cur = pth ; cur != null ; cur = cur.Parent) {
400-
if (cur.Value == method_call.Path.Value) {
401-
string linkNode = pth.Decomposed[depth];
402-
if (!linkNodes.Contains (linkNode)) {
403-
intro.WriteNode (linkNode);
404-
linkNodes.Add (linkNode);
416+
lock (registeredObjects) {
417+
foreach (ObjectPath pth in registeredObjects.Keys) {
418+
if (pth.Value == (method_call.Path.Value)) {
419+
ExportObject exo = (ExportObject) registeredObjects [pth];
420+
exo.WriteIntrospect (intro);
421+
} else {
422+
for (ObjectPath cur = pth; cur != null; cur = cur.Parent) {
423+
if (cur.Value == method_call.Path.Value) {
424+
string linkNode = pth.Decomposed [depth];
425+
if (!linkNodes.Contains (linkNode)) {
426+
intro.WriteNode (linkNode);
427+
linkNodes.Add (linkNode);
428+
}
405429
}
406430
}
407431
}
@@ -415,12 +439,14 @@ internal void HandleMethodCall (MessageContainer method_call)
415439
return;
416440
}
417441

418-
BusObject bo;
419-
if (registeredObjects.TryGetValue (method_call.Path, out bo)) {
420-
ExportObject eo = (ExportObject)bo;
421-
eo.HandleMethodCall (method_call);
422-
} else {
423-
MaybeSendUnknownMethodError (method_call);
442+
lock (registeredObjects) {
443+
BusObject bo;
444+
if (registeredObjects.TryGetValue(method_call.Path, out bo)) {
445+
ExportObject eo = (ExportObject)bo;
446+
eo.HandleMethodCall (method_call);
447+
} else {
448+
MaybeSendUnknownMethodError (method_call);
449+
}
424450
}
425451
}
426452

@@ -459,17 +485,19 @@ public void Register (ObjectPath path, object obj)
459485
eo.Registered = true;
460486

461487
//TODO: implement some kind of tree data structure or internal object hierarchy. right now we are ignoring the name and putting all object paths in one namespace, which is bad
462-
registeredObjects[path] = eo;
488+
lock (registeredObjects)
489+
registeredObjects[path] = eo;
463490
}
464491

465492
public object Unregister (ObjectPath path)
466493
{
467494
BusObject bo;
468495

469-
if (!registeredObjects.TryGetValue (path, out bo))
470-
throw new Exception ("Cannot unregister " + path + " as it isn't registered");
471-
472-
registeredObjects.Remove (path);
496+
lock (registeredObjects) {
497+
if (!registeredObjects.TryGetValue (path, out bo))
498+
throw new Exception ("Cannot unregister " + path + " as it isn't registered");
499+
registeredObjects.Remove (path);
500+
}
473501

474502
ExportObject eo = (ExportObject)bo;
475503
eo.Registered = false;

src/ExportObject.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ internal virtual void WriteIntrospect (Introspector intro)
6767
internal static MethodCaller GetMCaller (MethodInfo mi)
6868
{
6969
MethodCaller mCaller;
70-
if (!mCallers.TryGetValue (mi, out mCaller)) {
71-
mCaller = TypeImplementer.GenCaller (mi);
72-
mCallers[mi] = mCaller;
70+
lock (mCallers) {
71+
if (!mCallers.TryGetValue (mi, out mCaller)) {
72+
mCaller = TypeImplementer.GenCaller (mi);
73+
mCallers[mi] = mCaller;
74+
}
7375
}
7476
return mCaller;
7577
}
@@ -90,11 +92,7 @@ public virtual void HandleMethodCall (MessageContainer method_call)
9092
return;
9193
}
9294

93-
MethodCaller mCaller;
94-
if (!mCallers.TryGetValue (mi, out mCaller)) {
95-
mCaller = TypeImplementer.GenCaller (mi);
96-
mCallers[mi] = mCaller;
97-
}
95+
MethodCaller mCaller = GetMCaller (mi);
9896

9997
Signature inSig, outSig;
10098
bool hasDisposableList;

src/Protocol/PendingCall.cs

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,33 @@
77

88
namespace DBus.Protocol
99
{
10-
public class PendingCall : IAsyncResult
10+
public class PendingCall : IAsyncResult, IDisposable
1111
{
12-
Connection conn;
13-
Message reply;
14-
ManualResetEvent waitHandle;
15-
bool completedSync;
16-
bool keepFDs;
17-
12+
private Connection conn;
13+
private Message reply;
14+
private ManualResetEvent waitHandle = new ManualResetEvent (false);
15+
private bool completedSync = false;
16+
private bool keepFDs;
17+
private CancellationTokenSource stopWait = new CancellationTokenSource();
18+
1819
public event Action<Message> Completed;
1920

20-
public PendingCall (Connection conn) : this (conn, false) {}
21+
public PendingCall(Connection conn)
22+
: this (conn, false)
23+
{
24+
}
25+
2126
public PendingCall (Connection conn, bool keepFDs)
2227
{
2328
this.conn = conn;
2429
this.keepFDs = keepFDs;
2530
}
2631

32+
public void Dispose()
33+
{
34+
stopWait.Dispose ();
35+
}
36+
2737
internal bool KeepFDs
2838
{
2939
get {
@@ -33,36 +43,24 @@ internal bool KeepFDs
3343

3444
public Message Reply {
3545
get {
36-
if (reply != null)
37-
return reply;
38-
39-
if (Thread.CurrentThread == conn.mainThread) {
40-
while (reply == null)
41-
conn.HandleMessage (conn.Transport.ReadMessage ());
42-
43-
completedSync = true;
44-
45-
conn.DispatchSignals ();
46-
} else {
47-
if (waitHandle == null)
48-
Interlocked.CompareExchange (ref waitHandle, new ManualResetEvent (false), null);
49-
50-
while (reply == null)
51-
waitHandle.WaitOne ();
52-
53-
completedSync = false;
46+
while (reply == null) {
47+
try {
48+
conn.Iterate (stopWait.Token);
49+
}
50+
catch (OperationCanceledException) {
51+
}
5452
}
55-
5653
return reply;
57-
}
54+
}
55+
5856
set {
5957
if (reply != null)
6058
throw new Exception ("Cannot handle reply more than once");
61-
6259
reply = value;
6360

64-
if (waitHandle != null)
65-
waitHandle.Set ();
61+
waitHandle.Set ();
62+
63+
stopWait.Cancel ();
6664

6765
if (Completed != null)
6866
Completed (reply);
@@ -84,9 +82,6 @@ object IAsyncResult.AsyncState {
8482

8583
WaitHandle IAsyncResult.AsyncWaitHandle {
8684
get {
87-
if (waitHandle == null)
88-
waitHandle = new ManualResetEvent (false);
89-
9085
return waitHandle;
9186
}
9287
}

0 commit comments

Comments
 (0)