TBoldDbValidator never frees the worker threads it creates. TBoldDbValidatorThread.Create (Source/PMapper/Validator/BoldDbValidator.pas) starts the thread without setting FreeOnTerminate; TBoldDbValidatorThread.Execute ends with ThreadList.Remove(self); and nothing else references the thread afterwards - TBoldDbValidator.Destroy does FreeAndNil(fThreadList), which frees the list, not its items. One TThread object (plus its handle) leaks per validator thread per run, on both adapters.
Found while writing the end-to-end validator test for #92 (the same threads also leaked their extra database connection until #92).
Root cause
The validator uses fire-and-forget worker threads but keeps the "owner frees" lifetime model without an owner: the thread removes itself from the only list that knows about it and then simply ends. TBoldDbCopy, the other multi-threaded tool in the same layer, uses TThread.CreateAnonymousThread, which is FreeOnTerminate by construction, and does not have the problem.
Fix
TBoldDbValidatorThread.Create: FreeOnTerminate := True. The thread already removes itself from ThreadList at the end of Execute and the completion callback fires from inside the last thread before it frees itself, so no other code holds a reference that could dangle.
Files changed
Source/PMapper/Validator/BoldDbValidator.pas
UnitTest/Code/Persistence/Test.BoldDbValidator.pas (end-to-end test with a concrete validator thread)
Testing
TBoldDbValidatornever frees the worker threads it creates.TBoldDbValidatorThread.Create(Source/PMapper/Validator/BoldDbValidator.pas) starts the thread without settingFreeOnTerminate;TBoldDbValidatorThread.Executeends withThreadList.Remove(self); and nothing else references the thread afterwards -TBoldDbValidator.DestroydoesFreeAndNil(fThreadList), which frees the list, not its items. OneTThreadobject (plus its handle) leaks per validator thread per run, on both adapters.Found while writing the end-to-end validator test for #92 (the same threads also leaked their extra database connection until #92).
Root cause
The validator uses fire-and-forget worker threads but keeps the "owner frees" lifetime model without an owner: the thread removes itself from the only list that knows about it and then simply ends.
TBoldDbCopy, the other multi-threaded tool in the same layer, usesTThread.CreateAnonymousThread, which isFreeOnTerminateby construction, and does not have the problem.Fix
TBoldDbValidatorThread.Create:FreeOnTerminate := True. The thread already removes itself fromThreadListat the end ofExecuteand the completion callback fires from inside the last thread before it frees itself, so no other code holds a reference that could dangle.Files changed
Source/PMapper/Validator/BoldDbValidator.pasUnitTest/Code/Persistence/Test.BoldDbValidator.pas(end-to-end test with a concrete validator thread)Testing
TestValidatorThreadsCompleteAndFreeThemselves(final name) - a test validator whose threads run a trivial query on their extra connection,ThreadCount = 2, driven throughExecuteagainst the data module's second persistence handle; asserts the thread list is empty and a live-instance counter on the test thread class returns to 0 — allocation counts turned out unusable for this: FireDAC retains 1–2 blocks per thread that opens a connection until its manager finalizes. Failed withExpected [0] but got [2](TEndToEndValidatorThread x 2in the FastMM report) with the fix disabled