[fpc-pascal] Re: Multi-threaded project with few locks (no Thread.waitfor). Memory consumption keeps increasing on Ubuntu 10.10 x64

Jonas Maebe jonas.maebe at elis.ugent.be
Fri Oct 15 17:06:37 CEST 2010


On 15 Oct 2010, at 16:31, Andrew Brunner wrote:

> On Fri, Oct 15, 2010 at 9:24 AM, Jonas Maebe <jonas.maebe at elis.ugent.be 
> > wrote:
>> You replaced a bunch of semaphore create/lock/unlock/destroy  
>> operations with
>> calls to a function that does not do anything.
>
> I did not.  I REMOVED create/destroy/lock/unlock.  I ADDED calls to
> the ThreadManager's own functions for thread Resume and Suspend.

Yes, that's what I meant, because the ThreadManeger's SuspendThread  
function does not do anything under Unix.

> Did
> you try executing the suspend command?  What happened?

First of all, if you use tthread.create(true) (i.e., create a  
suspended thread), then the "execute" method will never be called:

***
{$mode delphi}
uses
   cthreads, classes, sysutils;

type
   tmythread = class(tthread)
     procedure execute;override;
   end;

procedure tmythread.execute;
begin
   writeln('executing');
end;

var
   c: tmythread;
begin
   writeln('creating thread suspended');
   c:=tmythread.create(true);
   sleep(500);
   writeln('resuming thread');
   c.resume;
   c.waitfor;
   c.free;
end.
***

Currently writes:
creating thread suspended
resuming thread
executing

With the patch, the last line is missing.

Secondly, ignoring this bug and suspending the thread inside itself:

***
{$mode delphi}
uses
   cthreads, classes, sysutils;

type
   tmythread = class(tthread)
     procedure execute;override;
   end;

procedure tmythread.execute;
begin
   suspend;
   writeln('executing');
end;

var
   c: tmythread;
begin
   writeln('creating thread suspended');
   c:=tmythread.create(false);
   sleep(500);
   writeln('resuming thread');
   c.resume;
   c.waitfor;
   c.free;
end.
***

Currently writes:
creating thread suspended
resuming thread
executing

With the patch:
creating thread suspended
executing
resuming thread

>> That barrier has nothing to do with "set values of variables before  
>> calling
>> the inherited create method to their own creation" (although it's not
>> entirely clear to me you mean by that). If you set values that the  
>> child
>> thread will read after that thread has already been created, this  
>> memory
>> barrier will not help either. That barrier is again about memory  
>> write
>> ordering, which could cause the newly started thread to not see all  
>> writes
>> performed by the parent thread *before* creating the child thread.
>
> Other than waiting for the values to be committed, I just don't see  
> it's value.

The whole value is in waiting for the values to be committed. Without  
the write barrier, this could e.g. happen:

a) in parent thread
* globalvar:=1;
* create_thread();

b) in child thread:
writeln(gobalvar); // prints 0, or whatever the value of globalvar was  
before the parent thread assigned "1" to it

That can even happen on x86 in its default mode, because this is a  
"Stores reordered after Loads" situation (but as mentioned below, it's  
unlikely to happen).

>> And the reason that's it's probably not required, is because  
>> there's so much
>> code in between that it's unlikely that any of those writes would  
>> still be
>> outstanding at that point. It's not "for the few that don't  
>> understand how
>> to create a thread".
>
> Exactly.  The other code being what?

If you mean the "code in between" by "the other code": any code  
executed between setting the global variable in the parent thread and  
the actual starting of the child thread (which includes all of the  
code in pthread_create, and in case of FPC in tthread.create and/or  
BeginThread, although in case of tthread.create there is also some  
extra communication between the parent thread and the child thread via  
the heap).


Jonas



More information about the fpc-pascal mailing list