1

I have been having exceptions crop in my application, either stack overflow or our of memory. They show up in different places, depending on when the system has had enough. To put it another way, running the app twice won’t lead to the same exception in the same place.

I have some timers which cause database access. The AnyDac d/b component guys tell me that I can't reuse a global TADConnection but have to allocate it dynamically in each timer handler, which I have done.

I just thought that I had had a d'oh! moment when I looked at the latest stack trace.

fMainForm.TMainForm.GetToolNumberFromContext($31846FB4)
fMainForm.TMainForm.Received_HEART_BEAT($249AEFD0)
IdCommandHandlers.TIdCommand.DoCommand
IdCommandHandlers.TIdCommandHandler.DoCommand(???,$31846FB4,'')
IdCommandHandlers.TIdCommandHandler.Check('HEART_BEAT',$31846FB4)
IdCommandHandlers.TIdCommandHandlers.HandleCommand($31846FB4,'HEART_BEAT') <===
uADDatSManager.TADDatSRow.SetBlobLength($7DA10FDC,0,$C18DDDC,10,0,1,False)
uADDatSManager.TADDatSRow.SetBlobData($7DA10FDC,0,$C18DDDC,10,False)
uADDatSManager.TADDatSRow.SetData(0,$C18DDDC,10)
uADPhysMySQL.TADPhysMySQLCommand.FetchRow($7D2F4F90,nil)
uADPhysMySQL.TADPhysMySQLCommand.InternalFetchRowSet($7D2F4F90,nil,50)
uADPhysManager.DoFetch(0,50,50,False)
uADPhysManager.TADPhysCommand.FetchBase($7D2F4F90,False)
uADPhysManager.TADPhysCommandAsyncFetch.Execute
uADStanAsync.TADStanAsyncExecutor.ExecuteOperation(False)
uADStanAsync.TADStanAsyncExecutor.Run
uADPhysManager.TADPhysCommand.ExecuteTask(TADPhysCommandAsyncFetch($7DA24FEC) as IADStanAsyncOperation,TADPhysCommandAsyncFetch($7DA24FF8) as IADStanAsyncHandler,True)
uADPhysManager.TADPhysCommand.Fetch($7D2F4F90,False,True)
uADCompClient.TADCustomCommand.Fetch($7D2F4F90,False,True)
uADCompClient.TADCustomTableAdapter.Fetch(False)
uADCompClient.TADAdaptedDataSet.DoFetch($7D2F4F90,False,fdDown)
uADCompDataSet.TADDataSet.InternalFetchRows(False,True,fdDown)
uADCompDataSet.TADDataSet.GetRecord($7DA1AFF4,gmNext,True)
Data.DB.TDataSet.GetNextRecord
Data.DB.TDataSet.GetNextRecords
Data.DB.TDataSet.SetBufferCount(???)
Data.DB.TDataSet.UpdateBufferCount
Data.DB.TDataSet.DoInternalOpen
Data.DB.TDataSet.OpenCursor(???)
uADCompDataSet.TADDataSet.OpenCursor(False)
uADCompClient.TADRdbmsDataSet.OpenCursor(False)
Data.DB.TDataSet.SetActive(???)
uADCompDataSet.TADDataSet.SetActive(True)
Data.DB.TDataSet.Open
uADCompClient.TADRdbmsDataSet.Open('SELECT * FROM tagged_chemicals',(...),(...))
uADCompClient.TADRdbmsDataSet.Open('SELECT * FROM tagged_chemicals')
fMainForm.TMainForm.CheckEndOfScheduleTimerTimer($B116FAC)
Vcl.ExtCtrls.TTimer.Timer
Vcl.ExtCtrls.TTimer.WndProc(???)
System.Classes.StdWndProc(133584,275,1,0)
:768a62fa ; C:\Windows\syswow64\USER32.dll
:768a6d3a USER32.GetThreadDesktop + 0xd7
:768a77c4 ; C:\Windows\syswow64\USER32.dll
:768a788a USER32.DispatchMessageW + 0xf
Vcl.Forms.TApplication.ProcessMessage(???)

I don't understand that marked line, the sudden switch from AnyDac to Indy code

IdCommandHandlers.TIdCommandHandlers.HandleCommand($31846FB4,'HEART_BEAT') <===
uADDatSManager.TADDatSRow.SetBlobLength($7DA10FDC,0,$C18DDDC,10,0,1,False)

Can someone please explain it? Thanks

My first thought was that Indy was interrupting AnyDac, perhaps because it called Applciation.ProcessMessages or similar, but I don't see that on the stack ...

But if it can do that, then can it interrupt "normal" non-timer handler code?

I was sure that I had it cracked and that the problem was that my TCP command handlers were reusing an AnyDac component used by something else ... then I looked at my code and saw that there is no database access in the command handlers or in anything that they call.

I am stumped. Does what I wrote even make sense? Can anyone offer any advice?

Thanks a 1,000,000 in advance for any help.

Mawg says reinstate Monica
  • 38,334
  • 103
  • 306
  • 551

1 Answers1

1

Indy's commands handlers are used by TIdCmdTCPServer and TIdCmdTCPClient, which are both multi-threaded components. The command handlers are invoked inside of worker threads that Indy creates internally. There is no way that a command handler can interrupt an operation that is running in a different thread.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    +1 Thanks for that, @RemyLebeau. But timer events can? And can you explain that stack dump? Just curious – Mawg says reinstate Monica Sep 27 '12 at 05:11
  • "There is no way that a command handler can interrupt an operation that is running in a different thread." Just to be clear, that means also the main thread? I.E. their event handlers don't get called until whatever has been running as a resutlt of Application.ProcessMessages() finishes? Totally non-preemptive? – Mawg says reinstate Monica Oct 01 '12 at 00:15
  • 1
    Command handlers are not message-driven. `Application.ProcessMessages()` in the main thread as no effect on them at all, and vice versa. They are running in different threads. The call stack you showed does not make sense, the command handlers cannot inject themselves into another thread's stack like that. Even if your `OnCommand` event handlers were performing sync'ed operations to the main thread, which can be affected by `ProcessMessages()`, you still would not see that kind of call stack occur. – Remy Lebeau Oct 01 '12 at 23:39
  • +1 @RemyLebeau, thanks. In summary "TCP Command Handlers cannot interrupt anything else and cannot run at the same time as anything else, including timer expiry handleers? - true? – Mawg says reinstate Monica Oct 02 '12 at 01:02
  • 1
    That is correct, yes. Timers would be running in a different thread than the commands handlers as well. – Remy Lebeau Oct 02 '12 at 08:22
  • Thanks, Remy - another answer awarded to you. – Mawg says reinstate Monica Oct 04 '12 at 01:42