33// See COPYING for details
44
55using System ;
6+ using System . Collections . Concurrent ;
67using System . Collections . Generic ;
78using System . IO ;
89using System . Threading ;
10+ using System . Threading . Tasks ;
911using System . Reflection ;
1012
1113namespace DBus
@@ -37,23 +39,24 @@ public class Connection
3739
3840 Dictionary < uint , PendingCall > pendingCalls = new Dictionary < uint , PendingCall > ( ) ;
3941 Queue < Message > inbound = new Queue < Message > ( ) ;
40- Dictionary < ObjectPath , BusObject > registeredObjects = new Dictionary < ObjectPath , BusObject > ( ) ;
42+ ConcurrentDictionary < ObjectPath , BusObject > registeredObjects = new ConcurrentDictionary < ObjectPath , BusObject > ( ) ;
43+ private readonly ReadMessageTask readMessageTask ;
4144
4245 public delegate void MonitorEventHandler ( Message msg ) ;
4346 public MonitorEventHandler Monitors ; // subscribe yourself to this list of observers if you want to get notified about each incoming message
4447
4548 protected Connection ( )
4649 {
47-
50+ readMessageTask = new ReadMessageTask ( this ) ;
4851 }
4952
50- internal Connection ( Transport transport )
53+ internal Connection ( Transport transport ) : this ( )
5154 {
5255 this . transport = transport ;
5356 transport . Connection = this ;
5457 }
5558
56- internal Connection ( string address )
59+ internal Connection ( string address ) : this ( )
5760 {
5861 OpenPrivate ( address ) ;
5962 Authenticate ( ) ;
@@ -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
@@ -215,27 +219,23 @@ internal virtual uint Send (Message msg)
215219 return msg . Header . Serial ;
216220 }
217221
218- //temporary hack
219- internal void DispatchSignals ( )
222+ public void Iterate ( )
220223 {
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- }
224+ Iterate ( new CancellationToken ( false ) ) ;
231225 }
232226
233- public void Iterate ( )
227+ public void Iterate ( CancellationToken stopWaitToken )
234228 {
235- Message msg = transport . ReadMessage ( ) ;
236-
237- HandleMessage ( msg ) ;
238- DispatchSignals ( ) ;
229+ if ( TryGetStoredSignalMessage ( out Message inboundMsg ) ) {
230+ try {
231+ HandleSignal ( inboundMsg ) ;
232+ } finally {
233+ inboundMsg . Dispose ( ) ;
234+ }
235+ } else {
236+ var msg = readMessageTask . MakeSureTaskRunAndWait ( stopWaitToken ) ;
237+ HandleMessage ( msg ) ;
238+ }
239239 }
240240
241241 internal virtual void HandleMessage ( Message msg )
@@ -251,21 +251,19 @@ internal virtual void HandleMessage (Message msg)
251251 try {
252252
253253 //TODO: Restrict messages to Local ObjectPath?
254-
255254 {
256- object field_value = msg . Header [ FieldCode . ReplySerial ] ;
255+ object field_value = msg . Header [ FieldCode . ReplySerial ] ;
257256 if ( field_value != null ) {
258257 uint reply_serial = ( uint ) field_value ;
259- PendingCall pending ;
260258
261259 lock ( pendingCalls ) {
260+ PendingCall pending ;
262261 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-
262+ if ( ! pendingCalls . Remove ( reply_serial ) )
263+ return ;
264+ pending . Reply = msg ;
265+ if ( pending . KeepFDs )
266+ cleanupFDs = false ; // caller is responsible for closing FDs
269267 return ;
270268 }
271269 }
@@ -285,8 +283,7 @@ internal virtual void HandleMessage (Message msg)
285283 break ;
286284 case MessageType . Signal :
287285 //HandleSignal (msg);
288- lock ( inbound )
289- inbound . Enqueue ( msg ) ;
286+ StoreInboundSignalMessage ( msg ) ; //temporary hack
290287 cleanupFDs = false ; // FDs are closed after signal is handled
291288 break ;
292289 case MessageType . Error :
@@ -391,14 +388,15 @@ internal void HandleMethodCall (MessageContainer method_call)
391388 //this is messy and inefficient
392389 List < string > linkNodes = new List < string > ( ) ;
393390 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 ] ;
391+ foreach ( var objKeyValuePair in registeredObjects ) {
392+ ObjectPath pth = objKeyValuePair . Key ;
393+ ExportObject exo = ( ExportObject ) objKeyValuePair . Value ;
394+ if ( pth . Value == method_call . Path . Value ) {
397395 exo . WriteIntrospect ( intro ) ;
398396 } else {
399- for ( ObjectPath cur = pth ; cur != null ; cur = cur . Parent ) {
397+ for ( ObjectPath cur = pth ; cur != null ; cur = cur . Parent ) {
400398 if ( cur . Value == method_call . Path . Value ) {
401- string linkNode = pth . Decomposed [ depth ] ;
399+ string linkNode = pth . Decomposed [ depth ] ;
402400 if ( ! linkNodes . Contains ( linkNode ) ) {
403401 intro . WriteNode ( linkNode ) ;
404402 linkNodes . Add ( linkNode ) ;
@@ -415,9 +413,8 @@ 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 ;
416+ if ( registeredObjects . TryGetValue ( method_call . Path , out BusObject bo ) ) {
417+ ExportObject eo = ( ExportObject ) bo ;
421418 eo . HandleMethodCall ( method_call ) ;
422419 } else {
423420 MaybeSendUnknownMethodError ( method_call ) ;
@@ -464,14 +461,10 @@ public void Register (ObjectPath path, object obj)
464461
465462 public object Unregister ( ObjectPath path )
466463 {
467- BusObject bo ;
468-
469- if ( ! registeredObjects . TryGetValue ( path , out bo ) )
464+ if ( ! registeredObjects . TryRemove ( path , out BusObject bo ) )
470465 throw new Exception ( "Cannot unregister " + path + " as it isn't registered" ) ;
471466
472- registeredObjects . Remove ( path ) ;
473-
474- ExportObject eo = ( ExportObject ) bo ;
467+ ExportObject eo = ( ExportObject ) bo ;
475468 eo . Registered = false ;
476469
477470 return eo . Object ;
@@ -486,6 +479,25 @@ internal protected virtual void RemoveMatch (string rule)
486479 {
487480 }
488481
482+ private void StoreInboundSignalMessage ( Message msg )
483+ {
484+ lock ( inbound ) {
485+ inbound . Enqueue ( msg ) ;
486+ }
487+ }
488+
489+ private bool TryGetStoredSignalMessage ( out Message msg )
490+ {
491+ msg = null ;
492+ lock ( inbound ) {
493+ if ( inbound . Count != 0 ) {
494+ msg = inbound . Dequeue ( ) ;
495+ return true ;
496+ }
497+ }
498+ return false ;
499+ }
500+
489501 static UUID ReadMachineId ( string fname )
490502 {
491503 byte [ ] data = File . ReadAllBytes ( fname ) ;
@@ -494,5 +506,31 @@ static UUID ReadMachineId (string fname)
494506
495507 return UUID . Parse ( System . Text . Encoding . ASCII . GetString ( data , 0 , 32 ) ) ;
496508 }
509+
510+ private class ReadMessageTask
511+ {
512+ private readonly Connection ownerConnection ;
513+ private Task < Message > task = null ;
514+ private object taskLock = new object ( ) ;
515+
516+ public ReadMessageTask ( Connection connection )
517+ {
518+ ownerConnection = connection ;
519+ }
520+
521+ public Message MakeSureTaskRunAndWait ( CancellationToken stopWaitToken )
522+ {
523+ Task < Message > catchedTask = null ;
524+
525+ lock ( taskLock ) {
526+ if ( task == null || task . IsCompleted ) {
527+ task = Task < Message > . Run ( ( ) => ownerConnection . transport . ReadMessage ( ) ) ;
528+ }
529+ catchedTask = task ;
530+ }
531+ catchedTask . Wait ( stopWaitToken ) ;
532+ return catchedTask . Result ;
533+ }
534+ }
497535 }
498536}
0 commit comments