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();

Attachments

cmemmod.pas test.pas

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information