Skip to content

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