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

Commit 46b987b

Browse files
committed
Fix race conditions is case of threaded Iterate() loop and methods call
1 parent 2e42286 commit 46b987b

File tree

4 files changed

+119
-98
lines changed

4 files changed

+119
-98
lines changed

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: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ public class Connection
4242
public delegate void MonitorEventHandler (Message msg);
4343
public MonitorEventHandler Monitors; // subscribe yourself to this list of observers if you want to get notified about each incoming message
4444

45+
private ManualResetEventSlim iterateEvent = MakeNewEventToNextIterate ();
46+
private readonly object iterateLocker = new object ();
47+
4548
protected Connection ()
4649
{
47-
4850
}
4951

5052
internal Connection (Transport transport)
@@ -230,12 +232,38 @@ internal void DispatchSignals ()
230232
}
231233
}
232234

233-
public void Iterate ()
235+
public void Iterate()
234236
{
235-
Message msg = transport.ReadMessage ();
237+
bool lockAcquired = false;
238+
239+
lock (iterateLocker) {
240+
if (iterateEvent.IsSet) {
241+
iterateEvent.Reset ();
242+
lockAcquired = true;
243+
}
244+
}
245+
246+
if (lockAcquired) {
247+
// One therad acquired a semi-lock. This thread read message from the stream.
248+
Message msg = transport.ReadMessage ();
249+
HandleMessage (msg);
236250

237-
HandleMessage (msg);
238-
DispatchSignals ();
251+
lock (iterateLocker) {
252+
iterateEvent.Set ();
253+
iterateEvent = MakeNewEventToNextIterate ();
254+
}
255+
256+
DispatchSignals ();
257+
}
258+
else {
259+
// All other treads wait the massage will be read and handeled.
260+
iterateEvent.Wait ();
261+
}
262+
}
263+
264+
private static ManualResetEventSlim MakeNewEventToNextIterate ()
265+
{
266+
return new ManualResetEventSlim (true);
239267
}
240268

241269
internal virtual void HandleMessage (Message msg)
@@ -251,21 +279,18 @@ internal virtual void HandleMessage (Message msg)
251279
try {
252280

253281
//TODO: Restrict messages to Local ObjectPath?
254-
255282
{
256-
object field_value = msg.Header[FieldCode.ReplySerial];
283+
object field_value = msg.Header [FieldCode.ReplySerial];
257284
if (field_value != null) {
258285
uint reply_serial = (uint)field_value;
259-
PendingCall pending;
260-
261286
lock (pendingCalls) {
287+
PendingCall pending;
262288
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-
289+
if (!pendingCalls.Remove (reply_serial))
290+
return;
291+
pending.Reply = msg;
292+
if (pending.KeepFDs)
293+
cleanupFDs = false; // caller is responsible for closing FDs
269294
return;
270295
}
271296
}
@@ -391,17 +416,19 @@ internal void HandleMethodCall (MessageContainer method_call)
391416
//this is messy and inefficient
392417
List<string> linkNodes = new List<string> ();
393418
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);
419+
lock (registeredObjects) {
420+
foreach (ObjectPath pth in registeredObjects.Keys) {
421+
if (pth.Value == (method_call.Path.Value)) {
422+
ExportObject exo = (ExportObject) registeredObjects [pth];
423+
exo.WriteIntrospect (intro);
424+
} else {
425+
for (ObjectPath cur = pth; cur != null; cur = cur.Parent) {
426+
if (cur.Value == method_call.Path.Value) {
427+
string linkNode = pth.Decomposed [depth];
428+
if (!linkNodes.Contains (linkNode)) {
429+
intro.WriteNode (linkNode);
430+
linkNodes.Add (linkNode);
431+
}
405432
}
406433
}
407434
}
@@ -415,12 +442,14 @@ internal void HandleMethodCall (MessageContainer method_call)
415442
return;
416443
}
417444

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);
445+
lock (registeredObjects) {
446+
BusObject bo;
447+
if (registeredObjects.TryGetValue(method_call.Path, out bo)) {
448+
ExportObject eo = (ExportObject)bo;
449+
eo.HandleMethodCall (method_call);
450+
} else {
451+
MaybeSendUnknownMethodError (method_call);
452+
}
424453
}
425454
}
426455

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

461490
//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;
491+
lock (registeredObjects)
492+
registeredObjects[path] = eo;
463493
}
464494

465495
public object Unregister (ObjectPath path)
466496
{
467497
BusObject bo;
468498

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

474505
ExportObject eo = (ExportObject)bo;
475506
eo.Registered = false;

src/Protocol/PendingCall.cs

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@ namespace DBus.Protocol
99
{
1010
public class PendingCall : IAsyncResult
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+
1818
public event Action<Message> Completed;
1919

20-
public PendingCall (Connection conn) : this (conn, false) {}
20+
public PendingCall(Connection conn)
21+
: this (conn, false)
22+
{
23+
}
24+
2125
public PendingCall (Connection conn, bool keepFDs)
2226
{
2327
this.conn = conn;
@@ -33,36 +37,17 @@ internal bool KeepFDs
3337

3438
public Message Reply {
3539
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;
54-
}
55-
40+
while (reply == null)
41+
conn.Iterate ();
5642
return reply;
57-
}
43+
}
44+
5845
set {
5946
if (reply != null)
6047
throw new Exception ("Cannot handle reply more than once");
61-
6248
reply = value;
6349

64-
if (waitHandle != null)
65-
waitHandle.Set ();
50+
waitHandle.Set ();
6651

6752
if (Completed != null)
6853
Completed (reply);
@@ -84,9 +69,6 @@ object IAsyncResult.AsyncState {
8469

8570
WaitHandle IAsyncResult.AsyncWaitHandle {
8671
get {
87-
if (waitHandle == null)
88-
waitHandle = new ManualResetEvent (false);
89-
9072
return waitHandle;
9173
}
9274
}

src/Protocol/Transport.cs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace DBus.Transports
1414
abstract class Transport
1515
{
1616
readonly object writeLock = new object ();
17+
readonly object readLock = new object ();
1718

1819
[ThreadStatic]
1920
static byte[] readBuffer;
@@ -154,12 +155,26 @@ protected void FireWakeUp ()
154155
WakeUp (this, EventArgs.Empty);
155156
}
156157

158+
159+
internal virtual void WriteMessage(Message msg)
160+
{
161+
lock (writeLock)
162+
{
163+
msg.Header.GetHeaderDataToStream (stream);
164+
if (msg.Body != null && msg.Body.Length != 0)
165+
stream.Write (msg.Body, 0, msg.Body.Length);
166+
stream.Flush ();
167+
}
168+
}
169+
157170
internal Message ReadMessage ()
158171
{
159172
Message msg;
160173

161174
try {
162-
msg = ReadMessageReal ();
175+
lock (readLock)
176+
msg = ReadMessageReal ();
177+
163178
if (msg == null) {
164179
if (connection.IsConnected) {
165180
if (ProtocolInformation.Verbose)
@@ -173,30 +188,17 @@ internal Message ReadMessage ()
173188
connection.IsConnected = false;
174189
msg = null;
175190
}
176-
191+
177192
if (connection != null && connection.Monitors != null)
178193
connection.Monitors (msg);
179194

180195
return msg;
181196
}
182197

183-
internal virtual int Read (byte[] buffer, int offset, int count, UnixFDArray fdArray)
184-
{
185-
int read = 0;
186-
while (read < count) {
187-
int nread = stream.Read (buffer, offset + read, count - read);
188-
if (nread == 0)
189-
break;
190-
read += nread;
191-
}
192-
193-
if (read > count)
194-
throw new Exception ();
195-
196-
return read;
197-
}
198-
199-
Message ReadMessageReal ()
198+
/// <summary>
199+
/// Thread unsafe! Safely called from <see cref="ReadMessage"/>.
200+
/// </summary>
201+
private Message ReadMessageReal ()
200202
{
201203
byte[] header = null;
202204
byte[] body = null;
@@ -266,18 +268,24 @@ Message ReadMessageReal ()
266268
}
267269

268270
Message msg = Message.FromReceivedBytes (Connection, header, body, fdArray);
269-
270271
return msg;
271272
}
272273

273-
internal virtual void WriteMessage (Message msg)
274+
internal virtual int Read (byte[] buffer, int offset, int count, UnixFDArray fdArray)
274275
{
275-
lock (writeLock) {
276-
msg.Header.GetHeaderDataToStream (stream);
277-
if (msg.Body != null && msg.Body.Length != 0)
278-
stream.Write (msg.Body, 0, msg.Body.Length);
279-
stream.Flush ();
276+
int read = 0;
277+
while (read < count)
278+
{
279+
int nread = stream.Read (buffer, offset + read, count - read);
280+
if (nread == 0)
281+
break;
282+
read += nread;
280283
}
284+
285+
if (read > count)
286+
throw new Exception ();
287+
288+
return read;
281289
}
282290

283291
// Returns true if then transport supports unix FDs, even when the

0 commit comments

Comments
 (0)