Recent

Author Topic: Howto send Record to thread via Pointer?  (Read 1757 times)

alpine

  • Hero Member
  • *****
  • Posts: 1063
Re: Howto send Record to thread via Pointer?
« Reply #15 on: December 11, 2022, 12:07:05 am »
Line 103 should be:
Code: Pascal  [Select][+][-]
  1.   TThread1.create(r);
Note the additional T in front. Without it you just call the create() with Self equal to the variable Thread1, which is Nil since all global variables are initialized with zeros at start.

Instances of a given class are created by calling a constructor prefixed by the class name (TThread1 in this case). The full statement is:
Code: Pascal  [Select][+][-]
  1.   Thread1 := TThread1.create(r);
but since in my example the instances were automatically freed, I don't assign them to a variable.

"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

jan741

  • New Member
  • *
  • Posts: 15
Re: Howto send Record to thread via Pointer?
« Reply #16 on: December 14, 2022, 05:06:54 pm »
What is the difference between

TMyThread.Create(r);

and using the new keyword? Pascal classes are made in the heap, I suppose, so after .create(r) you get a reference, I think to the initiated object on the heap, correct?
But when do you use the new keyword? And can you use it in this specific case? In C++ the constructor is called when you initiate with the new keyword not with calling the constructor directly.
A bit confusing.  %)

Thx

alpine

  • Hero Member
  • *****
  • Posts: 1063
Re: Howto send Record to thread via Pointer?
« Reply #17 on: December 14, 2022, 07:32:47 pm »
What is the difference between

TMyThread.Create(r);

and using the new keyword? Pascal classes are made in the heap, I suppose, so after .create(r) you get a reference, I think to the initiated object on the heap, correct?
Consider the following code:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyClass = class(TObject)
  3.   public
  4.     constructor Create;
  5.   end;
  6.  
  7. constructor TMyClass.Create;
  8. begin
  9.   inherited Create;
  10. end;
  11.  
  12. var
  13.   O: TMyClass;
  14. begin
  15.   O := TMyClass.Create;
  16.   O := O.Create;
  17. end.
  18.  

The line 15 (constructor with the class at the front) is compiled as:
Code: ASM  [Select][+][-]
  1. # [22] O := TMyClass.Create;
  2.         movl    $1,%edx
  3.         movl    $VMT_$P$UNIT1_$$_TMYCLASS,%eax
  4.         call    P$UNIT1$_$TMYCLASS_$__$$_CREATE$$TMYCLASS
  5.         movl    %eax,U_$P$UNIT1_$$_O
The edx is loaded with 1 and the eax with the VMT pointer. The thing is that when edx is preloaded with 1, then the generated Create calls the NewInstance entry from the given VMT, usually TObject.NewInstance unless overriden. See lines 15,16:
Code: ASM  [Select][+][-]
  1. P$PROGRAM$_$TMYCLASS_$__$$_CREATE$$TMYCLASS:
  2. # [unit1.pas]
  3. # [8] begin
  4.         pushl   %ebp
  5.         movl    %esp,%ebp
  6.         leal    -12(%esp),%esp
  7.         pushl   %ebx
  8.         pushl   %esi
  9.         pushl   %edi
  10. # Var $vmt located at ebp-4, size=OS_32
  11. # Var $self located at ebp-8, size=OS_32
  12. # Var $vmt_afterconstruction_local located at ebp-12, size=OS_S32
  13.         movl    %eax,-8(%ebp)
  14.         movl    %edx,-4(%ebp)
  15.         cmpl    $1,%edx
  16.         jne     .Lj6
  17.         movl    -8(%ebp),%eax
  18.         movl    -8(%ebp),%edx
  19.         call    *52(%edx) ;<---------------------- NewInstance into VMT
  20.         movl    %eax,-8(%ebp)
  21.         .balign 4,0x90
  22. .Lj6:
  23.         cmpl    $0,-8(%ebp)
  24.  

NewInstance allocates needed memory from heap via GetMem, calls InitInstance, then returns it into eax register (which is actually the Self).

At the other hand, if called with a  variable instead of a class in the front (line 16):
Code: ASM  [Select][+][-]
  1.         movl    %eax,U_$P$PROGRAM_$$_O
  2. # [16] O := O.Create;
  3.         movl    $-1,%edx
  4.         call    P$PROGRAM$_$TMYCLASS_$__$$_CREATE$$TMYCLASS
  5.         movl    %eax,U_$P$PROGRAM_$$_O
  6. # [17] end.
  7.  
Here the edx is preloaded with -1 (not 1) and the eax with the value of the variable O, i.e. the Self implicit argument. With the edx not equal to 1, the call to NewInstance gets skipped, assuming eax is already pointing to the instance memory.

That way:

  • You can call the constructor qualified with the class name, thus allocating a new memory chunk for the instance on the heap before the first line of the Create constructor gets executed, you may think the constructors are actually a class methods in that case
      OR
  • You can preallocate memory by other means, assign the pointer to it to the variable, call  InitInstance to set the VMT pointer, and then call the constructor as a regular method qualified by a variable name.

The former is the usual way the instances were created in Object Pascal, the latter (or variation of it) can be used if you want some special allocation scheme, say to reuse an instance once allocated and to skip FreeMem/GetMem pair.

But when do you use the new keyword? And can you use it in this specific case? In C++ the constructor is called when you initiate with the new keyword not with calling the constructor directly.
A bit confusing.  %)

The C++ objects are constructed implicitly and if you want them on the heap you must use the new/delete operators. In contrast the FPC objects are actually just references and if you want them constructed you must do that explicitly.

In FPC new/dispose aren't used for that purpose. They're remnants from the original Pascal and not very much used nowadays.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

jan741

  • New Member
  • *
  • Posts: 15
Re: Howto send Record to thread via Pointer?
« Reply #18 on: December 15, 2022, 10:49:30 am »
Ok thanks.

How did you get the assembly code? I would also like to see the memory-layout on given address. I tried to use windbg, but can't connect because the debugger within lazarus is already connected to the process.

Thanks

alpine

  • Hero Member
  • *****
  • Posts: 1063
Re: Howto send Record to thread via Pointer?
« Reply #19 on: December 15, 2022, 11:07:11 am »
Ok thanks.

How did you get the assembly code? I would also like to see the memory-layout on given address. I tried to use windbg, but can't connect because the debugger within lazarus is already connected to the process.

Thanks
Project Options --> Compiler Options --> Custom Options --> press the button [All options ...] --> check -al option.
Then generated *.s file(s) will be left into the output directory.

For the memory dump at address - I haven't found a feasible way yet. Will be happy to know.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: Howto send Record to thread via Pointer?
« Reply #20 on: December 15, 2022, 01:54:01 pm »
I pointed out several days ago that doing this has to be approached carefully, using critical sections etc.  I've also said that I'm extremely dubious about trusting the heap manager for this, it's really not what it's designed for.

If there's one producer and multiple consumers, then either some sort of reference counting needs to be done, or each consumer needs a separate buffer.

Attached is something I've been tinkering with over the last few days which might serve as a useful example: a thread-safe extensible circular buffer. While this is byte-based, it is trivially extensible to any fixed-size data structure and could probably be redone using generics.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018