InitThread in memory manager is not called when new thread is created
Summary
When thread is started (for example using BeginThread
), InitThread
routine of memory manager is not called; however DoneThread
routine is always called (and even is not ifdef'ed in thread.inc
).
System Information
- Operating system: Linux (Ubuntu 24.04, kernel 6.8.0-35-generic (64-bit))
- Processor architecture: x86-64
- Compiler version: trunk
- Device: Computer
Steps to reproduce
Simplest route: copy cmem module and add CInitThread
and CDoneThread
routines to it that only write messages to stdout, like so:
// cmem body...
procedure CInitThread;
begin
WriteLn('!!! Init thread');
end;
procedure CDoneThread;
begin
WriteLn('!!! Done thread');
end;
Const
CMemoryManager : TMemoryManager =
(
NeedLock : false;
GetMem : @CGetmem;
FreeMem : @CFreeMem;
FreememSize : @CFreememSize;
AllocMem : @CAllocMem;
ReallocMem : @CReAllocMem;
MemSize : @CMemSize;
InitThread : @CInitThread;
DoneThread : @CDoneThread;
RelocateHeap : nil;
GetHeapStatus : @CGetHeapStatus;
GetFPCHeapStatus: @CGetFPCHeapStatus;
);
In general - just create memory manager that handles InitThread
(it will not be called)
Example Project
Example project using modified cmem unit (called cmemmod
):
program test;
{$Mode Delphi}
uses
cmemmod,
{$IfDef Unix}
cthreads,
{$EndIf}
SysUtils;
function ProcTask(AData: Pointer): PtrInt;
begin
WriteLn('In task!');
Result := 0;
end;
var ThreadId: TThreadId;
begin
WriteLn('Starting thread');
ThreadId := BeginThread(@ProcTask, nil);
WriteLn('Waiting');
WaitForThreadTerminate(ThreadId, -1);
WriteLn('Done');
end.
What is the current bug behavior?
Code outputs this:
Starting thread
Waiting
In task!
!!! Done thread
Done
What is the expected (correct) behavior?
It should call InitThread
in memory manager and therefore print !!! Init thread
:
Starting thread
Waiting
!!! Init thread <-- this line could be before "Waiting", it does not matter; it just must be before "In task!"
In task!
!!! Done thread
Done
Possible fixes
In thread.inc
in procedure InitThread(stklen: SizeUInt)
there is a condition:
{$ifndef HAS_MEMORYMANAGER}
{$ifndef FPC_NO_DEFAULT_HEAP}
{ initialize this thread's heap }
InitHeapThread;
{$endif ndef FPC_NO_DEFAULT_HEAP}
{$else HAS_MEMORYMANAGER}
if MemoryManager.InitThread <> nil then
MemoryManager.InitThread();
{$endif HAS_MEMORYMANAGER}
HAS_MEMORYMANAGER
is disabled in heap.inc
, therefore memory manager never gets a chance to get InitThread
call.
However in DoneThread
in thread.inc
, call to DoneThread
of memory manager is not ifdef'ed and is always called:
{$ifndef HAS_MEMORYMANAGER}
{$ifndef FPC_NO_DEFAULT_HEAP}
FinalizeHeap;
{$endif ndef FPC_NO_DEFAULT_HEAP}
{$endif HAS_MEMORYMANAGER}
if MemoryManager.DoneThread <> nil then
MemoryManager.DoneThread();