Recent

Author Topic: Dereferencing an object handed into a thread by Pointer  (Read 754 times)

RedOctober

  • Sr. Member
  • ****
  • Posts: 460
Dereferencing an object handed into a thread by Pointer
« on: November 28, 2024, 09:57:25 pm »
Lazarus 3.4 (rev lazarus_3_4) FPC 3.2.2 x86_64-win64-win32/win64
OS: Windows Server 2016
Component in use:  Brook 5 Framework

Background:  I am trying to access parameter values that I have handed into an isolated thread of the Brook 5 ARequest.Isolate() function.  I have included the pertinent sections of code here, and my question is at the end.

// Type Declaration:

Code: Pascal  [Select][+][-]
  1. type
  2.   TISO_Parms = class
  3.     seg_txt_rbi: String;
  4.     req_lst, rsp_lst, fli_lst, flo_lst: TStringList;
  5.     sys_err: Boolean;
  6.     sys_err_txt: String;
  7.     db_data_rqrd: String;
  8.     dmISO: TDataModule;
  9.   end;
  10.  

// Prepare an instance of ISO_Parms to hold all our stuff

Code: Pascal  [Select][+][-]
  1. ISO_Parms := TISO_Parms.Create;
  2.  
  3.       ISO_Parms.seg_txt_rbi := seg_txt_rbi;
  4.  
  5.       ISO_Parms.req_lst := TStringList.Create;
  6.       ISO_Parms.rsp_lst := TStringList.Create;
  7.       ISO_Parms.fli_lst := TStringList.Create;
  8.       ISO_Parms.flo_lst := TStringList.Create;
  9.  
  10.       ISO_Parms.req_lst.AddStrings(req_lst);
  11.       ISO_Parms.rsp_lst.AddStrings(rsp_lst);
  12.       ISO_Parms.fli_lst.AddStrings(fli_lst);
  13.       ISO_Parms.flo_lst.AddStrings(flo_lst);
  14.  
  15.       ISO_Parms.sys_err := sys_err;
  16.       ISO_Parms.sys_err_txt := sys_err_txt;
  17.       ISO_Parms.db_data_rqrd := db_data_rqrd;
  18.       ISO_Parms.dmISO := nil;  // I'll create this later, inside the thread
  19.  

// Call the "Isolator" procedure

Code: Pascal  [Select][+][-]
  1. ARequest.Isolate(@GetAllDashItems_ISO);
  2.  

// Isolated procedure I am having trouble with

Code: Pascal  [Select][+][-]
  1. procedure GetAllDashItems_ISO(ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse; AUserData: Pointer);
  2. begin
  3.   GetAllDashItemsIsolated(ARequest, AResponse,
  4.    
  5. AUserData^.ISO_Parms.seg_txt_rbi,  // <-- Getting error here:  Illegal Qualifier
  6.  
  7. // ... of course, all the lines below have the same problem as the line above
  8. AUserData.req_lst, AUserData.rsp_lst, AUserData.fli_lst, AUserData.flo_lst,
  9.     AUserData.sys_err, AUserData.sys_err_txt, AUserData.db_data_rqrd,
  10.     AUserData.dmISO);
  11. end;
  12.  

Question:

What is the proper syntax for dereferencing (accessing) the values in the parameters contained in my ISO_Parms object.  I have built isolated threads in the Brook 5 Framework before, with success, but I have never before included anything in the AUserData parameter.

Thanks in advance for any help you can provide.

TRon

  • Hero Member
  • *****
  • Posts: 3623
Re: Dereferencing an object handed into a thread by Pointer
« Reply #1 on: November 28, 2024, 10:05:34 pm »
And what is passed as AUserdata then ? That generic pointer should be cast to whatever was passed there.

fwiw: Isolate has a second parameter for the userdata (and the code shown does not seem to use it).
« Last Edit: November 28, 2024, 10:13:26 pm by TRon »
This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

MarkMLl

  • Hero Member
  • *****
  • Posts: 8028
Re: Dereferencing an object handed into a thread by Pointer
« Reply #2 on: November 28, 2024, 10:12:58 pm »
A question like this with isolated fragments quite simply doesn't give us enough to work with, but first apply an assertion that the parameter is the specified class, and then cast it using as.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

RedOctober

  • Sr. Member
  • ****
  • Posts: 460
Re: Dereferencing an object handed into a thread by Pointer
« Reply #3 on: November 28, 2024, 10:48:52 pm »
Hi Mark and Ron.  I'm making progress, based on your responses, but I'm not quite there yet.
Here is the pertinent source code from Brook:

Code: Pascal  [Select][+][-]
  1.  
  2.     procedure Isolate(AProc: TBrookHTTPRequestIsolatedProc;
  3.       AUserData: Pointer = nil);{$IFNDEF FPC}overload;{$ENDIF}virtual;
  4.  
  5.     { Isolates a request from the main event loop to an own dedicated thread,
  6.       bringing it back when the request finishes.
  7.       @param(AProc[in] Anonymous Procedure to handle requests and responses
  8.       isolated from the main event loop.)
  9.       @param(AUserData[in] User-defined data.) }
  10.     procedure Isolate(const AProc: TBrookHTTPRequestIsolatedAnonymousProc;
  11.       AUserData: Pointer = nil); overload; virtual;
  12.  
  13.  
  14.   { TBrookHTTPReqIsolatedProcHolder }
  15.  
  16.   TBrookHTTPReqIsolatedProcHolder<T> = class
  17.   private
  18.     FProc: T;
  19.     FUserData: Pointer;
  20.   public
  21.     constructor Create(const AProc: T; AUserData: Pointer);
  22.     property Proc: T read FProc;
  23.     property UserData: Pointer read FUserData;
  24.   end;
  25.  
  26. { TBrookHTTPReqIsolatedProcHolder }
  27.  
  28. constructor TBrookHTTPReqIsolatedProcHolder<T>.Create(const AProc: T;
  29.   AUserData: Pointer);
  30. begin
  31.   inherited Create;
  32.   FProc := AProc;
  33.   FUserData := AUserData;
  34. end;
  35.  
  36. procedure TBrookHTTPRequest.Isolate(AProc: TBrookHTTPRequestIsolatedProc;
  37.   AUserData: Pointer);
  38. var
  39.   VHolder: TBrookHTTPReqIsolatedProcHolder<TBrookHTTPRequestIsolatedProc>;
  40. begin
  41.   SgLib.Check;
  42.   SetIsIsolated(True);
  43.   VHolder := TBrookHTTPReqIsolatedProcHolder<
  44.     TBrookHTTPRequestIsolatedProc>.Create(AProc, AUserData);
  45.   try
  46.     SgLib.CheckLastError(sg_httpreq_isolate(FHandle,
  47.       DoRequestIsolatedProcCallback, VHolder));
  48.   except
  49.     VHolder.Free;
  50.     raise;
  51.   end;
  52. end;
  53.  
  54.  

I modified my code a bit, as shown below.  Now it compiles but the variables contain nothing.  Still stuck.

Code: Pascal  [Select][+][-]
  1. procedure GetAllDashItems_ISO(ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse; AUserData: Pointer);
  2. var s: String;
  3. begin
  4.   // The section below compiles, but the thing cast as TISO_Parms contain nothing, and give a memory access error
  5.   s := TISO_Parms(AUserData).seg_txt_rbi;
  6.   ShowMessage('s: ' + s);  // This just displays "s: " with nothing after it.
  7.  
  8.   // The section below compiles, but the things cast as TISO_Parms contain nothing, and give a memory access error
  9.   GetAllDashItemsIsolated(ARequest, AResponse,
  10.     TISO_Parms(AUserData).seg_txt_rbi,
  11.     TISO_Parms(AUserData).req_lst,
  12.     TISO_Parms(AUserData).rsp_lst,
  13.     TISO_Parms(AUserData).fli_lst,
  14.     TISO_Parms(AUserData).flo_lst,
  15.     TISO_Parms(AUserData).sys_err,
  16.     TISO_Parms(AUserData).sys_err_txt,
  17.     TISO_Parms(AUserData).db_data_rqrd);
  18. end;
  19.  

I also changed how I call it:

Code: Pascal  [Select][+][-]
  1.       ARequest.Isolate(@GetAllDashItems_ISO, @ISO_Parms);
  2.  



RedOctober

  • Sr. Member
  • ****
  • Posts: 460
Re: Dereferencing an object handed into a thread by Pointer
« Reply #4 on: November 28, 2024, 10:54:00 pm »
I think I've got it now.  I just had to change this last thing, using the modified code shown above:


Code: Pascal  [Select][+][-]
  1.       ARequest.Isolate(@GetAllDashItems_ISO, ISO_Parms);
  2.  

Seems the last parameter must not be a pointer, only the first one.

TRon

  • Hero Member
  • *****
  • Posts: 3623
Re: Dereferencing an object handed into a thread by Pointer
« Reply #5 on: November 28, 2024, 10:58:15 pm »
I think I've got it now.  I just had to change this last thing, using the modified code shown above:


Code: Pascal  [Select][+][-]
  1.       ARequest.Isolate(@GetAllDashItems_ISO, ISO_Parms);
  2.  

Seems the last parameter must not be a pointer, only the first one.
Both are pointers but how to indicate depends on the compiler mode (because first pointer is a pointer to a function (technically the address of the function) ). The second pointer can be anything you want to, in your case a pointer (technically in this case the address of the class instance) to a class.

PS: it might perhaps help to have a look at the help of the isolate method.
« Last Edit: November 28, 2024, 11:12:47 pm by TRon »
This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

cdbc

  • Hero Member
  • *****
  • Posts: 1655
    • http://www.cdbc.dk
Re: Dereferencing an object handed into a thread by Pointer
« Reply #6 on: November 28, 2024, 11:27:41 pm »
Hi
This seems correct:
Code: Pascal  [Select][+][-]
  1.      ARequest.Isolate(@GetAllDashItems_ISO, ISO_Parms);
...and this usage Needs a bit:
Code: Pascal  [Select][+][-]
  1. procedure GetAllDashItems_ISO(ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse; AUserData: Pointer);
  2. var
  3.   liso: TISO_Parms; ///// (l)ocal(iso) var /////
  4. begin
  5. ///// here typecast to a (l)ocal(iso) variable /////
  6. ///// reason for the 'FUserData' we find in: /////
  7. ///// 'TBrookHTTPReqIsolatedProcHolder<T>.Create' //////
  8.   liso:= TISO_Parms(ARequest.FUserData); /// copied in isolate()
  9. ///// below we then use the local varible /////
  10.   GetAllDashItemsIsolated(ARequest, AResponse,
  11.     liso.seg_txt_rbi,
  12.     liso.req_lst,
  13.     liso.rsp_lst,
  14.     liso.fli_lst,
  15.     liso.flo_lst,
  16.     liso.sys_err,
  17.     liso.sys_err_txt,
  18.     liso.db_data_rqrd);
  19. ///// behind the scenes a TObject is just an elaborate pointer /////
  20. end;
I think that should do it...
Regards Benny
« Last Edit: November 28, 2024, 11:40:02 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

cdbc

  • Hero Member
  • *****
  • Posts: 1655
    • http://www.cdbc.dk
Re: Dereferencing an object handed into a thread by Pointer
« Reply #7 on: November 28, 2024, 11:41:52 pm »
Hi
I changed my post a few times after, beware...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

RedOctober

  • Sr. Member
  • ****
  • Posts: 460
Re: Dereferencing an object handed into a thread by Pointer
« Reply #8 on: November 28, 2024, 11:44:02 pm »
Hi cdbc,

Should I be .Free ing the local instance of liso, like this...

Code: Pascal  [Select][+][-]
  1.   ...
  2.  
  3.   liso:= TISO_Parms(ARequest.FUserData);
  4.  
  5.   GetAllDashItemsIsolated(ARequest, AResponse,
  6.     liso.seg_txt_rbi,
  7.     liso.req_lst,
  8.     liso.rsp_lst,
  9.     liso.fli_lst,
  10.     liso.flo_lst,
  11.     liso.sys_err,
  12.     liso.sys_err_txt,
  13.     liso.db_data_rqrd);
  14.  
  15.     liso.Free;
  16.  
  17. ...
  18.  
  19.  



or will the isolated thread free itself, and everything in it, when it completes?

cdbc

  • Hero Member
  • *****
  • Posts: 1655
    • http://www.cdbc.dk
Re: Dereferencing an object handed into a thread by Pointer
« Reply #9 on: November 28, 2024, 11:48:15 pm »
Hi
H*LL NO, no freeing, you are just borrowing it!!!!
The freeing takes place outside of the thread, in the thread you created it  in(possibly the main thread)...
I will add, that the others are right, it's more difficult for us, with only a few bits of code...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Khrys

  • Full Member
  • ***
  • Posts: 105
Re: Dereferencing an object handed into a thread by Pointer
« Reply #10 on: November 29, 2024, 07:06:49 am »
Code: Pascal  [Select][+][-]
  1. type
  2.   TISO_Parms = class
  3.     seg_txt_rbi: String;
  4.     req_lst, rsp_lst, fli_lst, flo_lst: TStringList;
  5.     sys_err: Boolean;
  6.     sys_err_txt: String;
  7.     db_data_rqrd: String;
  8.     dmISO: TDataModule;
  9.   end;
  10.  

Code: Pascal  [Select][+][-]
  1. procedure GetAllDashItems_ISO(ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse; AUserData: Pointer);
  2. begin
  3.   GetAllDashItemsIsolated(ARequest, AResponse,
  4.    
  5. AUserData^.ISO_Parms.seg_txt_rbi,  // <-- Getting error here:  Illegal Qualifier
  6.  
  7. // ... of course, all the lines below have the same problem as the line above
  8. AUserData.req_lst, AUserData.rsp_lst, AUserData.fli_lst, AUserData.flo_lst,
  9.     AUserData.sys_err, AUserData.sys_err_txt, AUserData.db_data_rqrd,
  10.     AUserData.dmISO);
  11. end;
  12.  

What is the proper syntax for dereferencing (accessing) the values in the parameters contained in my ISO_Parms object.  I have built isolated threads in the Brook 5 Framework before, with success, but I have never before included anything in the AUserData parameter.

You'll need to cast  AUserData  to the correct type to dereference it; trying to dereference a  Pointer  is the same as trying to dereference a  void*  in C - it's simply not possible.
This kind of untyped pointer commonly occurs in user-defined callback functions (such as yours here) to allow passing arbitrary data, at the expense of compiler-provided type checking.

But there's another caveat -  TISO_Params  is a class type, and class instances themselves are just pointers in Free Pascal, so you can just use a normal cast:

Code: Pascal  [Select][+][-]
  1. TISO_Params(AUserData).seg_txt_rbi



If  TISO_Params  was a  record  instead, you'd have to declare its pointer type as well, such that you're able to cast & dereference manually:

Code: Pascal  [Select][+][-]
  1. type
  2.   TISO_Params: record
  3.     seg_txt_rbi: String;
  4.     req_lst, rsp_lst, fli_lst, flo_lst: TStringList;
  5.     sys_err: Boolean;
  6.     sys_err_txt: String;
  7.     db_data_rqrd: String;
  8.     dmISO: TDataModule;
  9.   end;
  10.   PISO_Params = ^TISO_Params;
  11.  
  12. procedure GetAllDashItems_ISO(ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse; AUserData: Pointer);
  13. begin
  14.   GetAllDashItemsIsolated(ARequest, AResponse,
  15.                           PISO_PARAMS(AUserData)^.seg_txt_rbi,
  16.   // [...]
  17. end;

RedOctober

  • Sr. Member
  • ****
  • Posts: 460
Re: Dereferencing an object handed into a thread by Pointer
« Reply #11 on: November 29, 2024, 04:39:54 pm »
Hi KHrys, I have made the change in my source code to make ISO_Params a reccord instead of an Object.  That makes sense to me, because it relieves me of having to create, then free, an object.  However, I am having trouble handing the pointer to the isolated function.  See my code below:'

I start by declaring the types you show in your example:

Code: Pascal  [Select][+][-]
  1. type
  2.   TISO_Params = record
  3.     seg_txt_rbi: String;
  4.     req_lst, rsp_lst, fli_lst, flo_lst: TStringList;
  5.     sys_err: Boolean;
  6.     sys_err_txt: String;
  7.     db_data_rqrd: String;
  8.   end;
  9.   PISO_Params = ^TISO_Params;
  10.  

I then declare a local copy of the TISO_Params, and populate it with the data contained in the variables in the main thread (this is the stuff that must end up in the isolated function, via the pointer).  The ARequest.Isolate() line is where the compiler shows an error "Illegal Type Conversion, TISO_Params to PISO_params"

Code: Pascal  [Select][+][-]
  1. procedure MyMainThreadProc(...);
  2.   ...
  3.   var ISO_Params: TISO_Params;
  4.   ...
  5. begin
  6.  
  7.       ISO_Params.seg_txt_rbi := seg_txt_rbi;
  8.       ISO_Params.req_lst := req_lst;
  9.       ISO_Params.rsp_lst := rsp_lst;
  10.       ISO_Params.fli_lst := fli_lst;
  11.       ISO_Params.flo_lst := flo_lst;
  12.       ISO_Params.sys_err := sys_err;
  13.       ISO_Params.sys_err_txt := sys_err_txt;
  14.       ISO_Params.db_data_rqrd := db_data_rqrd;
  15.  
  16.     ARequest.Isolate(@GetAllDashItems_ISO, PISO_Params(ISO_Params)); // Trouble here
  17. end;
  18.  


The trouble line is:

ARequest.Isolate(@GetAllDashItems_ISO, PISO_Params(ISO_Params));


So my question is:


ARequest.Isolate(@GetAllDashItems_ISO, <what goes here?>);



Thanks in advance. 

dseligo

  • Hero Member
  • *****
  • Posts: 1408
Re: Dereferencing an object handed into a thread by Pointer
« Reply #12 on: November 29, 2024, 04:46:07 pm »
Maybe:
ARequest.Isolate(@GetAllDashItems_ISO, PISO_Params(@ISO_Params));

RedOctober

  • Sr. Member
  • ****
  • Posts: 460
Re: Dereferencing an object handed into a thread by Pointer
« Reply #13 on: November 29, 2024, 05:02:35 pm »
I have made the following modifications.  Both compile, but now the problem is dereferencing:

Code: Pascal  [Select][+][-]
  1. ...
  2.   var ISO_Params: TISO_Params; Ptr: ^PISO_Params;
  3. ...
  4. begin
  5. ...
  6.       // Prepare an instance of ISO_Parms to hold all our stuff
  7.       ISO_Params.seg_txt_rbi := seg_txt_rbi;
  8.       ISO_Params.req_lst := req_lst;
  9.       ISO_Params.rsp_lst := rsp_lst;
  10.       ISO_Params.fli_lst := fli_lst;
  11.       ISO_Params.flo_lst := flo_lst;
  12.       ISO_Params.sys_err := sys_err;
  13.       ISO_Params.sys_err_txt := sys_err_txt;
  14.       ISO_Params.db_data_rqrd := db_data_rqrd;
  15. ...
  16. // This compiles
  17.      Ptr := @ISO_Params;
  18.      ARequest.Isolate(@GetAllDashItems_ISO, Ptr);
  19.  
  20. // This also compiles
  21.    //ARequest.Isolate(@GetAllDashItems_ISO, Ptr);
  22.    ARequest.Isolate(@GetAllDashItems_ISO,
  23.     PISO_Params(@ISO_Params));
  24.  
  25. // See the next code clip
  26. ...
  27. end;
  28.  

When dereferenced, the "thd_"  (local isolated thread) variables contain garbage, or cause a memory access error.

Code: Pascal  [Select][+][-]
  1. procedure GetAllDashItems_ISO(ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse; AUserData: Pointer);
  2. var
  3.   thd_seg_txt_rbi: String;
  4.   thd_req_lst,
  5.   thd_rsp_lst,
  6.   thd_fli_lst,
  7.   thd_flo_lst: TStringList;
  8.   thd_sys_err: Boolean;
  9.   thd_sys_err_txt,
  10.   thd_db_data_rqrd: String;
  11.  
  12. begin
  13.   thd_req_lst := TStringList.Create;
  14.   thd_rsp_lst := TStringList.Create;
  15.   thd_fli_lst := TStringList.Create;
  16.   thd_flo_lst := TStringList.Create;
  17.  
  18.   // Trouble starts here
  19.   thd_seg_txt_rbi := PISO_Params(AUserData)^.seg_txt_rbi;
  20.   thd_sys_err := PISO_Params(AUserData)^.sys_err;
  21.   thd_sys_err_txt := PISO_Params(AUserData)^.sys_err_txt;
  22.   thd_db_data_rqrd := PISO_Params(AUserData)^.db_data_rqrd;
  23.  
  24.   thd_req_lst.AddStrings(PISO_Params(AUserData)^.req_lst);
  25.   thd_rsp_lst.AddStrings(PISO_Params(AUserData)^.rsp_lst);
  26.   thd_fli_lst.AddStrings(PISO_Params(AUserData)^.fli_lst);
  27.   thd_flo_lst.AddStrings(PISO_Params(AUserData)^.flo_lst);
  28.  
  29.   GetAllDashItemsIsolated(
  30.     ARequest, AResponse,
  31.     thd_seg_txt_rbi,
  32.     thd_req_lst,
  33.     thd_rsp_lst,
  34.     thd_fli_lst,
  35.     thd_flo_lst,
  36.     thd_sys_err,
  37.     thd_sys_err_txt,
  38.     thd_db_data_rqrd);
  39.  
  40.   thd_req_lst.Free;
  41.   thd_rsp_lst.Free;
  42.   thd_fli_lst.Free;
  43.   thd_flo_lst.Free;
  44.  
  45. end;
  46.  
  47.  

 

TinyPortal © 2005-2018