<div dir="ltr">I am experiencing a weird issue regarding threading and shared libraries<br>under Linux. I've boiled it down to a very simple test case in hopes you<br>can help me understand what I'm doing wrong.<br><br>I'm running this test with FPC 3.0 (built from source) on x86_64 Debian Linux.<br><br>First, I have this shared library:<br><br>===========================================================================<br>library fpcso1;<br><br>{$mode objfpc}{$H+}<br><br>uses<br>  cthreads,<br>  // cmem,<br>  SysUtils;<br><br>procedure Blow; cdecl;<br>var<br>  p : pbyte;<br>begin<br>  p := GetMem(1024);<br>  FreeMem(p);<br>  p := nil;<br>end;<br><br>exports<br>  Blow name 'fpcso1_blow';<br><br>end.<br>===========================================================================<br><br>It is used by this program:<br>===========================================================================<br>program fpcprog;<br><br>{$mode objfpc}{$H+}<br><br>uses<br>  cthreads,<br>  cmem,<br>  Classes,<br>  SysUtils,<br>  DateUtils;<br><br>const<br>  lib1Name = 'libfpcso1.so';<br><br>procedure fpcso1_blow; cdecl; external lib1Name;<br><br>var<br>  finishedThreads : longint = 0;<br>  runningThreads : longint = 0;<br>  startedThreads : longint = 0;<br><br>type<br>  TTestThread = class(TThread)<br>  protected<br>    procedure Execute; override;<br>  public<br>    constructor Create;<br>  end;<br><br>  constructor TTestThread.Create;<br>  begin<br>    inherited Create(true, 1024*1024);<br>    FreeOnTerminate := true;<br>  end;<br><br>  procedure TTestThread.Execute;<br>  begin<br>    InterLockedIncrement(runningThreads);<br>    try<br>      fpcso1_blow();<br>    finally<br>      InterLockedIncrement(finishedThreads);<br>      InterLockedDecrement(runningThreads);<br>    end;<br>  end;<br><br>procedure BeginTestThread();<br>var<br>  tmpThread : TTestThread;<br>begin<br>  tmpThread := TTestThread.Create();<br>  tmpThread.Start;<br>end;<br><br>const<br>  threadCount = 100000;<br>  threadMaxConcurrent = 30;<br>var<br>  lastTime : TDateTime;<br>  lastCount : longint = 0;<br>begin<br>  lastCount := 0;<br>  lastTime := now;<br>  while (startedThreads < threadCount) do begin<br>    if (runningThreads < threadMaxConcurrent) then begin<br>      InterLockedIncrement(startedThreads);<br>      BeginTestThread();<br>    end else begin<br>      sleep(1);<br>    end;<br>    if MilliSecondsBetween(lastTime, now) >= 1000 then begin<br>      writeln(runningThreads, ' threads runningThreads, ',<br>                finishedThreads, ' threads finished (',<br>                finishedThreads-lastCount, '/sec)');<br>      lastCount := finishedThreads;<br>      lastTime := now;<br>    end;<br>  end;<br>  while (runningThreads > 0) do begin<br>    sleep(1000);<br>    writeln(runningThreads, ' threads runningThreads, ', finishedThreads,<br>              ' threads finished (', finishedThreads-lastCount, '/sec)');<br>    lastCount := finishedThreads;<br>  end;<br>  sleep(1000);<br>  writeln(runningThreads, ' threads runningThreads, ',<br>            finishedThreads, ' threads finished');<br><br>end.<br>===========================================================================<br><br>When I execute the program, over the course of the ten seconds it takes<br>to run, I watch the process' memory usage grow to over a gigabyte:<br><br> 1s  235.81 MiB<br> 2s  351.34 MiB<br> 3s  463.64 MiB<br> 4s  575.35 MiB<br> 5s  688.54 MiB<br> 6s  799.47 MiB<br> 7s  916.26 MiB<br> 8s    1.01 GiB<br> 9s    1.12 GiB<br>10s    1.15 GiB<br><br>However, if I uncomment the "// cmem," line in the library, the memory does<br>not grow.<br><br>Why does using the default memory manager seem to leak memory like crazy if<br>I'm doing heavy threading? I'm setting FreeOnTerminate to true in conjunction<br>with creating the thread suspended, then calling Start, which the<br>examples in the wiki and documentation seems to indicate is valid.<br><br>Is cmem *required* if I'm using threading on Linux? I hope not, because<br>while this example is boiled down to as simple as I could make it, I'm<br>having the same issue in my production application, in which I'm using my<br>own power-of-two memory manager, and I would really like to figure out<br>what I can do to continue to use my own memory manager, plus threading,<br>without it leaking so much memory for every thread that starts/finishes.<br><br>Thanks,<br><br>-Seth Grover</div>