Мסž
Мסž

Reputation:

main thread "blocked on critical section which is abandoned"

I've got a multithreaded app using the OmniThreadLibrary. I am misusing the OTL to open ADO stored procedures in secondary threads which works unless there's an error (and mostly even then). Unfortunately in this particular case there's a problem.

When I open a particular form I get an exception in a thread "There must be at least one field" copying a dataset into a kb memory table, which I handle and send a message to a monitoring thread. That message arrives and is stored successfully in the database. When I close that form the main VCL thread hangs.

kbMemTable.LoadFromDataset(StoredProc, []); // throws

Pausing the app in the debugger and looking at the thread list the main VCL thread shows:

"Blocked on critical section which is abandoned owned by Process 0"

The OTL threads are still live and time out of the thread pool, so it seems that everything is working except the main thread. I'm also using DevExpress and Raise components which have their own threads but don't name them (and don't seem to be part of the problem) which means I have 12 threads of which only 5 are identifiable.

I strongly suspect that something in the Delphi database has grabbed that critical section then failed to release it due to the exception. There don't seem to be any critical sections in the Delphi/database source code units that I'm using directly, but obviously there's one there somewhere.

This involves way too much source code to include, and my test app doesn't display the behaviour.

I'm asking for any tips on tracking this down.

My current thought is to switch to debug dcu's and breakpoint every critical section creation I can find, then see what happens. I could address the problem that's throwing the first exception, but I'm concerned that some other exception could have the same effect out in the field where it would be a pain to deal with. So I'd like to fix this problem first.

Edit: the critical section is owned by the thread that calls TADOStroredProc.ExecProc. That thread completes normally after handling the exception, so unless I leap into the debugger quickly the thread ages out of the pool before I see it, hence the ProcessID=0 above. Setting the thread linger time to 60s instead of 10s at least gives me that.

Upvotes: 1

Views: 1185

Answers (2)

Viktor Svub
Viktor Svub

Reputation: 1469

First, I don't see why you're calling opening an ADO stored proc in a background thread a "misuse" - it should work flawlessly, unless you have some mess in the different threads' apartments or other messaging (eg the monitoring thread notifications).

I don't know what exactly causes your problem, but I will share some experiences on the Delphi ADO multithreading field. The COM/OLE STA messaging is the main suspect here.

Background threads should really better be as MTA. I don't know if Delphi 2010 does this by itself, but Delphi 2006 doesn't - to make sure, look in the sources for calls to CoInitializeEx - if not called w/ the COINIT_MULTITHREADED=$00 flag, the thread is considered apartment-neutral and marshals through the STA/main thread. Every other init method will get you a nice STA thread.

You should:

  • provide your backround threads with proper COM ini-/finalization as MTA
  • use all ADO components only from the apartment which created them
  • not create other STA threads (main thread is one by default); combining the messaging w/ other locking is jist too much of a PITA
  • never use the ADO async abilities together with multithreading

Upvotes: 2

Edwin Yip
Edwin Yip

Reputation: 4220

I suggest you to use a logging library such as CodeSite to track down where the main thread is stopped.

Upvotes: 1

Related Questions