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

Commit b46d945

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

File tree

6 files changed

+179
-129
lines changed

6 files changed

+179
-129
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: 103 additions & 56 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
@@ -38,22 +39,23 @@ public class Connection
3839
Dictionary<uint,PendingCall> pendingCalls = new Dictionary<uint,PendingCall> ();
3940
Queue<Message> inbound = new Queue<Message> ();
4041
Dictionary<ObjectPath,BusObject> registeredObjects = new Dictionary<ObjectPath,BusObject> ();
42+
private readonly ReadMessageTask readMessageTask;
4143

4244
public delegate void MonitorEventHandler (Message msg);
4345
public MonitorEventHandler Monitors; // subscribe yourself to this list of observers if you want to get notified about each incoming message
4446

4547
protected Connection ()
4648
{
47-
49+
readMessageTask = new ReadMessageTask (this);
4850
}
4951

50-
internal Connection (Transport transport)
52+
internal Connection (Transport transport) : this ()
5153
{
5254
this.transport = transport;
5355
transport.Connection = this;
5456
}
5557

56-
internal Connection (string address)
58+
internal Connection (string address) : this ()
5759
{
5860
OpenPrivate (address);
5961
Authenticate ();
@@ -183,11 +185,12 @@ internal uint GenerateSerial ()
183185

184186
internal Message SendWithReplyAndBlock (Message msg, bool keepFDs)
185187
{
186-
PendingCall pending = SendWithReply (msg, keepFDs);
187-
return pending.Reply;
188+
using (PendingCall pending = SendWithPendingReply (msg, keepFDs)) {
189+
return pending.Reply;
190+
}
188191
}
189192

190-
internal PendingCall SendWithReply (Message msg, bool keepFDs)
193+
internal PendingCall SendWithPendingReply (Message msg, bool keepFDs)
191194
{
192195
msg.ReplyExpected = true;
193196

@@ -215,27 +218,23 @@ internal virtual uint Send (Message msg)
215218
return msg.Header.Serial;
216219
}
217220

218-
//temporary hack
219-
internal void DispatchSignals ()
221+
public void Iterate ()
220222
{
221-
lock (inbound) {
222-
while (inbound.Count != 0) {
223-
Message msg = inbound.Dequeue ();
224-
try {
225-
HandleSignal (msg);
226-
} finally {
227-
msg.Dispose ();
228-
}
229-
}
230-
}
223+
Iterate (new CancellationToken (false));
231224
}
232225

233-
public void Iterate ()
226+
public void Iterate (CancellationToken stopWaitToken)
234227
{
235-
Message msg = transport.ReadMessage ();
236-
237-
HandleMessage (msg);
238-
DispatchSignals ();
228+
if (TryGetStoredSignalMessage (out Message inboundMsg)) {
229+
try {
230+
HandleSignal (inboundMsg);
231+
} finally {
232+
inboundMsg.Dispose ();
233+
}
234+
} else {
235+
var msg = readMessageTask.MakeSureTaskRunAndWait (stopWaitToken);
236+
HandleMessage (msg);
237+
}
239238
}
240239

241240
internal virtual void HandleMessage (Message msg)
@@ -251,21 +250,19 @@ internal virtual void HandleMessage (Message msg)
251250
try {
252251

253252
//TODO: Restrict messages to Local ObjectPath?
254-
255253
{
256-
object field_value = msg.Header[FieldCode.ReplySerial];
254+
object field_value = msg.Header [FieldCode.ReplySerial];
257255
if (field_value != null) {
258256
uint reply_serial = (uint)field_value;
259-
PendingCall pending;
260257

261258
lock (pendingCalls) {
259+
PendingCall pending;
262260
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-
261+
if (!pendingCalls.Remove (reply_serial))
262+
return;
263+
pending.Reply = msg;
264+
if (pending.KeepFDs)
265+
cleanupFDs = false; // caller is responsible for closing FDs
269266
return;
270267
}
271268
}
@@ -285,8 +282,7 @@ internal virtual void HandleMessage (Message msg)
285282
break;
286283
case MessageType.Signal:
287284
//HandleSignal (msg);
288-
lock (inbound)
289-
inbound.Enqueue (msg);
285+
StoreInboundSignalMessage (msg); //temporary hack
290286
cleanupFDs = false; // FDs are closed after signal is handled
291287
break;
292288
case MessageType.Error:
@@ -391,17 +387,19 @@ internal void HandleMethodCall (MessageContainer method_call)
391387
//this is messy and inefficient
392388
List<string> linkNodes = new List<string> ();
393389
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);
390+
lock (registeredObjects) {
391+
foreach (ObjectPath pth in registeredObjects.Keys) {
392+
if (pth.Value == (method_call.Path.Value)) {
393+
ExportObject exo = (ExportObject) registeredObjects [pth];
394+
exo.WriteIntrospect (intro);
395+
} else {
396+
for (ObjectPath cur = pth; cur != null; cur = cur.Parent) {
397+
if (cur.Value == method_call.Path.Value) {
398+
string linkNode = pth.Decomposed [depth];
399+
if (!linkNodes.Contains (linkNode)) {
400+
intro.WriteNode (linkNode);
401+
linkNodes.Add (linkNode);
402+
}
405403
}
406404
}
407405
}
@@ -415,12 +413,14 @@ internal void HandleMethodCall (MessageContainer method_call)
415413
return;
416414
}
417415

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);
416+
lock (registeredObjects) {
417+
BusObject bo;
418+
if (registeredObjects.TryGetValue(method_call.Path, out bo)) {
419+
ExportObject eo = (ExportObject)bo;
420+
eo.HandleMethodCall (method_call);
421+
} else {
422+
MaybeSendUnknownMethodError (method_call);
423+
}
424424
}
425425
}
426426

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

461461
//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;
462+
lock (registeredObjects)
463+
registeredObjects[path] = eo;
463464
}
464465

465466
public object Unregister (ObjectPath path)
466467
{
467468
BusObject bo;
468469

469-
if (!registeredObjects.TryGetValue (path, out bo))
470-
throw new Exception ("Cannot unregister " + path + " as it isn't registered");
471-
472-
registeredObjects.Remove (path);
470+
lock (registeredObjects) {
471+
if (!registeredObjects.TryGetValue (path, out bo))
472+
throw new Exception ("Cannot unregister " + path + " as it isn't registered");
473+
registeredObjects.Remove (path);
474+
}
473475

474476
ExportObject eo = (ExportObject)bo;
475477
eo.Registered = false;
@@ -486,6 +488,25 @@ internal protected virtual void RemoveMatch (string rule)
486488
{
487489
}
488490

491+
private void StoreInboundSignalMessage (Message msg)
492+
{
493+
lock (inbound) {
494+
inbound.Enqueue (msg);
495+
}
496+
}
497+
498+
private bool TryGetStoredSignalMessage (out Message msg)
499+
{
500+
msg = null;
501+
lock (inbound) {
502+
if (inbound.Count != 0) {
503+
msg = inbound.Dequeue ();
504+
return true;
505+
}
506+
}
507+
return false;
508+
}
509+
489510
static UUID ReadMachineId (string fname)
490511
{
491512
byte[] data = File.ReadAllBytes (fname);
@@ -494,5 +515,31 @@ static UUID ReadMachineId (string fname)
494515

495516
return UUID.Parse (System.Text.Encoding.ASCII.GetString (data, 0, 32));
496517
}
518+
519+
private class ReadMessageTask
520+
{
521+
private readonly Connection ownerConnection;
522+
private Task<Message> task = null;
523+
private object taskLock = new object ();
524+
525+
public ReadMessageTask (Connection connection)
526+
{
527+
ownerConnection = connection;
528+
}
529+
530+
public Message MakeSureTaskRunAndWait(CancellationToken stopWaitToken)
531+
{
532+
Task<Message> catchedTask = null;
533+
534+
lock (taskLock) {
535+
if (task == null || task.IsCompleted) {
536+
task = Task<Message>.Run (() => ownerConnection.transport.ReadMessage ());
537+
}
538+
catchedTask = task;
539+
}
540+
catchedTask.Wait (stopWaitToken);
541+
return catchedTask.Result;
542+
}
543+
}
497544
}
498545
}

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;

0 commit comments

Comments
 (0)