5

Indy 10:

two threads, thread 1 is calling Get on an TIdHTTP and blocked reading data. thread 2 will call disconnect on the same TIdHTTP in order to interrupt the Get.

I'm using Digest Authentication on the TIdHTTP and i get an AV ocassionally.

callstack for thread 1:

40491D [system.pas][System][@ClassDestroy][8989]
69EF2B [..\..\common\IdAuthenticationDigest.pas][IdAuthenticationDigest][TIdDigestAuthentication.Destroy][109]
404567 [system.pas][System][TObject.Free][8313]
6A2B69 [..\..\Common\IdHTTP.pas][IdHTTP][TIdCustomHTTP.DoOnDisconnected][1587]
534574 [IdTCPConnection.pas][IdTCPConnection][TIdTCPConnection.Disconnect][532]
534B3B [IdTCPConnection.pas][IdTCPConnection][TIdTCPConnection.Disconnect][767]
6A3FB3 [..\..\Common\IdHTTP.pas][IdHTTP][TIdCustomHTTP.DoRequest][2101]

callstack for thread 2:

402BA3 [system.pas][System][@FreeMem][2477]
404521 [system.pas][System][TObject.FreeInstance][8294]
40491D [system.pas][System][@ClassDestroy][8989]
69EF2B [..\..\common\IdAuthenticationDigest.pas][IdAuthenticationDigest]        [TIdDigestAuthentication.Destroy][109]
404567 [system.pas][System][TObject.Free][8313]
6A2B69 [..\..\Common\IdHTTP.pas][IdHTTP][TIdCustomHTTP.DoOnDisconnected][1587]
534574 [IdTCPConnection.pas][IdTCPConnection][TIdTCPConnection.Disconnect][532]
534B3B [IdTCPConnection.pas][IdTCPConnection][TIdTCPConnection.Disconnect][767]

basically at the end of the DoRequest it drops into a disconnect. seems like there's a race condition on the disconnects trying to free Request.Authentication.

downloaded the latest code for Indy 10 and looking at the code i believe the behavior should be the same.

i believe calling disconnect from another thread is the recommended usage pattern, am i wrong? is this a bug in Indy? seems like it would be necessary to lock the disconnect but hard to see what deadlocks that might create. anyone else ran into this?

Mike Davis
  • 197
  • 11
  • you can't use Indy like this. You are supposed to manipulate the object in one thread. What are you trying to achieve here? – whosrdaddy Sep 20 '12 at 18:19
  • i'm not sure, see this thread for example, it seems as if this was the expected way to interrupt a get request. http://www.delphigroups.info/2/4/208119.html – Mike Davis Sep 20 '12 at 18:50
  • @whosrdaddy: not true. Indy is designed to support this kind of usage. In fact, some of Indy's own components, like `TIdTelnet`, do exactly the same thing internally that Mike is doing. Unless you use timeouts, disconnecting the socket from another thread is the only way to abort a blocked I/O operation. – Remy Lebeau Sep 20 '12 at 18:50
  • So this must be a locking bug then? – whosrdaddy Sep 20 '12 at 19:00

1 Answers1

6

Yes, what you are encountering is a race condition with both threads trying to free the same Request.Authentication object twice when the socket is being disconnected. Given the stack traces, both threads would have to be disconnecting the socket at exactly the same time, because Disconnect() only calls DoOnDisconnect() if the IOHandler is still open, and the IOHandler is closed before DoOnDisconnect() is called.

What you could try doing is use the OnStatus event to enter a thread-safe lock such as a critical section or mutex in the hsDisconnecting state, and leave the lock in the hsDisconnected state. IOHandler.Close() and DoOnDisconnect() are called in between those states, so that would effectively serialize your threads so they cannot disconnect the socket and free the Request.Authentication object at the exact same time anymore.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    thanks, that is more elegant than what i had in mind. i'll give it a try under the same stress scenarios but it looks good to me. – Mike Davis Sep 20 '12 at 20:40