Jump to content
mikerabat

Error message: "'Cannot call Start on a running or suspended thread"

Recommended Posts

Hi all!

 

In our system we load a bunch of files on startup in parallel so for each of these files we craete a thread

that does that in the background.

 

basically this is what how it is done:

TEVentChannel is the data object that holds the data

-> on the data object creation a thread (TEvtLoadThread = class(TThread)) is created which does the loading

-> of course it is ensured that data access and loading is mutal exclusive.

The snippset of code that is involved is:

 

constructor TEventChannel.TEvtLoadThread.Create(parent: TEventChannel);
begin
     fParent := parent;

     inherited Create(False);
end;

procedure TEventChannel.TEvtLoadThread.Execute;
begin
     NameThreadForDebugging('Load '+AnsiString(fParent.ChanName));
     // ###########################################
     // #### Just start loading the data
     try
        fParent.Reload;
     except
           on E : EChannelException do
           begin
                fParent.fLoadFailed := False;
                fParent.fDataLoaded := lsBadRecId;
           end;
     else
         // damaged file??
         fParent.fDataLoaded := lsAll;
         fParent.Clear;

         fParent.fLoadFailed := True;
     end;
end;

 

My problem here is that on a system on which that code ran for over 2 years without problems now happens to fail with the error

"Cannot call Start on a running or suspended thread"

This error is issued in the TThread.Afterconstruction call and it seems that our system fails to create that thread...

 

Looking at the TThread code it is most likely that the Thread could not be created so what on earth could be could interfere here?

 

We tried to disable FSecure (or at least they claim no security system is monitoring our program) and Windows Defender without any change.

Is there anything known out there that could limit an external program in such way?

 

kind regards

  Mike

 

 

 

Share this post


Link to post

Create the thread suspended and then start it once the constructor returns. It is generally a bad idea to construct a thread with CreateSuspended=False.

 

I.e. instead of:

constructor TEventChannel.TEvtLoadThread.Create(parent: TEventChannel);
begin
  fParent := parent;
  inherited Create(False);
end;

do this:

constructor TEventChannel.TEvtLoadThread.Create(parent: TEventChannel);
begin
  inherited Create(True);
  fParent := parent;
end;
...
  TheThread := TEvtLoadThread.Create(Something);
  TheThread.Start;
...

 

Share this post


Link to post
Posted (edited)
3 hours ago, mikerabat said:

The snippset of code that is involved is:

Offhand, the code looks fine to me.  I've never had any problems with using TThread with CreateSuspended=False.  However, as a workaround, you can try creating the TThread with CreateSuspended=True, and then manually call TThread.Start() after the constructor has exited.  If that works, then I would suspect that Embarcdero may have possibly introduced a regression in TThread's construction.  What version of Delphi are you using?

Quote

My problem here is that on a system on which that code ran for over 2 years without problems now happens to fail with the error

"Cannot call Start on a running or suspended thread"

This error is issued in the TThread.Afterconstruction call and it seems that our system fails to create that thread...

On Windows, the TThread constructor creates a thread with the CREATE_SUSPENDED flag enabled, then the TThread.AfterConstruction() method calls ResumeThread() if the TThread constructor was called with CreateSuspended=False (as is the case in your example).  The error message you are seeing is raised if ResumeThread() fails to resume the thread, ie either because the API failed outright (ResumeThread() returns -1), or the thread was suspended multiple times (ResumeThread() returns > 1), neither of which should be happening in your example.

 

Are you, by chance, calling TThread.Start() anywhere else in your code?  If so, you should not be calling Start() on a TThread created with CreateSuspended=False.

Quote

Looking at the TThread code it is most likely that the Thread could not be created

If that were the case, you would be getting an exception from the TThread constructor, not its AfterConstruction() method.

Edited by Remy Lebeau

Share this post


Link to post
4 minutes ago, Anders Melander said:

Create the thread suspended and then start it once the constructor returns. It is generally a bad idea to construct a thread with CreateSuspended=False.

In general, there is nothing wrong with creating a TThread with CreateSuspended=False. That has worked just fine for the past 2 decades.  So, unless Embarcadero has recently broken TThread, something else is likely going on.

 

  • Like 2

Share this post


Link to post
9 minutes ago, Remy Lebeau said:

That has worked just fine for the past 2 decades.

You're right. I need to flush my long term memory of obsolete information 🙂.

In Delphi 5 the tread was started immediately in the TThread constructor. Since Delphi 6 it is started in AfterConstruction.

  • Like 1

Share this post


Link to post

Thanks Remy!!

 

Right... the call to BeginThread actually suceeds - the callstack clearly indicates that it's the ResumeThread call that fails.

So... the only thing I found online was that there is a right  " THREAD_SUSPEND_RESUME " which may have

some influence here... But I have absolutely no idea what could change that.

Note that the code actually works on around 8k of installations but there are only a few (2 for now) that seem to

make trouble in that regard. The thing is that if there are way fewer files to be loaded (e.g. 6-8) it works most of the time

(aka not always)... Also the system worked for 2 years now without any problems... so I'm quite stunned that now something pops up.

 

So... Is there any 3rd party or any configurable restrictions on Windows that disallow something like releasing many threads at once?

 

kind regards

  Mike

 

Share this post


Link to post
Posted (edited)
Quote

So... the only thing I found online was that there is a right  " THREAD_SUSPEND_RESUME " which may have

some influence here... But I have absolutely no idea what could change that.

Not relevant in this case, because the handle returned by CreateThread() has THREAD_ALL_ACCESS permissions, which includes THREAD_SUSPEND_RESUME.  That permission is meant to prevent other people from suspending/resuming threads they don't have access to.  You have full access to your own threads.

Quote

Note that the code actually works on around 8k of installations but there are only a few (2 for now) that seem to

make trouble in that regard.

You are just going to have to debug your app on these failing systems to see what ResumeThread() is actually returning:

  • -1 = failure, what does GetLastError() report?
  • 0 = was not suspended to begin with
  • 1 = was suspended, now resumed (this is what you want in this situation)
  • > 1 = was and still is suspended
Quote

So... Is there any 3rd party or any configurable restrictions on Windows that disallow something like releasing many threads at once?

Not that I'm aware of.  The number of threads you can create is limited only by available memory.  Even if there were a configurable limit, you would be getting failures in CreateThread(), not in ResumeThread().

Edited by Remy Lebeau

Share this post


Link to post

Thanks for all the input.

Finally it turned out that a white listing software from https://seculution.de/

was the problem at all. They thought we did some bad stuff and their heuristics failed

miserably.

Please note that they also tried to just disable that software to rule them out but disabling was not enough ☹️

 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×