delphidelphi-xe6

How to set stack size in TThread?


How can I set a custom stack size in TThread? I am trying to reintroduce the constructor of TThread but it says that ThreadProc is missing yet its right there in System.Classes.

type
  TThreadHelper = class helper for TThread
    constructor Create(const CreateSuspended: Boolean = False; const StackSize: Integer = 0); reintroduce;
 end;

{ TThreadHelper }

constructor TThreadHelper.Create(const CreateSuspended: Boolean; const StackSize: Integer);
begin
  Self.FSuspended := not Self.FExternalThread;
  Self.FCreateSuspended := CreateSuspended and not Self.FExternalThread;
  if not Self.FExternalThread then
  begin
    Self.FHandle := BeginThread(nil, StackSize, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, Self.FThreadID);
    if Self.FHandle = 0 then
    raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
  end
  else
  begin
    Self.FHandle := Winapi.Windows.GetCurrentThread;
    Self.FThreadId := GetCurrentThreadId;
  end;
end;

[dcc32 Error] Project5.dpr(29): E2003 Undeclared identifier: 'ThreadProc'


Solution

  • I do not know, if you can set stack size after a thread is created. Maybe SetThreadStackGuarantee can be helpful?

    You can create a thread from scratch by using BeginThread, but it is quite complicated. I have here a workaround by using Detours. Note that there are several Detours variants. I think only the Cromis.Detours is x64 compatible.

    unit IndividualStackSizeForThread;
    
    interface
    
    uses 
      System.Classes,
      Cromis.Detours { http://www.cromis.net/blog/downloads/cromis-ipc/ };
    
    type
      TThreadHelper = class helper for TThread
        constructor Create(CreateSuspended: Boolean; StackSize: LongWord);
     end;
    
    implementation
    
    var
      TrampolineBeginThread: function(SecurityAttributes: Pointer; StackSize: LongWord;
        ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord; 
        var ThreadId: TThreadID): THandle = nil;
    
    threadvar
      StackSizeOverride: LongWord;
    
    function InterceptBeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
      ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
      var ThreadId: TThreadID): THandle;
    const
      STACK_SIZE_PARAM_IS_A_RESERVATION = $00010000; // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx
    begin
      if StackSizeOverride <> 0 then
      begin
        CreationFlags := CreationFlags or STACK_SIZE_PARAM_IS_A_RESERVATION;
        StackSize := StackSizeOverride;
        StackSizeOverride := 0;
      end;
    
      Result := TrampolineBeginThread(SecurityAttributes, StackSize, ThreadFunc, 
        Parameter, CreationFlags, ThreadId);
    end;
    
    constructor TThreadHelper.Create(CreateSuspended: Boolean; StackSize: LongWord);
    begin
      StackSizeOverride := StackSize;
      inherited Create(CreateSuspended);
    end;
    
    initialization
    
    TrampolineBeginThread := InterceptCreate(@BeginThread, @InterceptBeginThread);
    
    finalization
    
    InterceptRemove(@TrampolineBeginThread, @InterceptBeginThread);
    
    end.
    

    I do not know why Embt does not allow programmer to specify the stack size, if someone knows the reason, it will be very interesting to me.