Recent

Author Topic: Threading OSX and DYLIBs  (Read 231 times)

kevin.black

  • Full Member
  • ***
  • Posts: 122
Threading OSX and DYLIBs
« on: June 13, 2019, 05:28:40 am »
MBP with parallels Mojave VM lazarus 2.0.0RC3

I have read the tutorial docs WRT threading and it appears quite straight forward. I say that noting that so far, everything I have tried on OSX either hasn't worked or required some not so obvious surgery to get it to work. Threading under windows seems to work fine, on OSX not so much. Again, I have pursued the forum and wiki and not getting much out of it. Here are my code snippets, most of which is straight out of the tutorial:

Code: Pascal  [Select]
  1. {$mode objfpc}{$H+}
  2. {$ModeSwitch pChartoString}
  3. {$define UseCThreads}
  4.  
  5. interface
  6.  
  7. uses
  8.   {$IFDEF UNIX}
  9.   {$IFDEF UseCThreads}
  10.   cthreads,
  11.   {$ENDIF}
  12.   {$ENDIF}
  13.   Interfaces,
  14. ...
  15. ...
  16. const
  17.   {$IFDEF MSWINDOWS}
  18.   EmpyreanAPIDLL = 'DbXServerAPI.dll';
  19.   libEmpyreanCrypto = 'libEmpyreanCrypto.dll';
  20.   {$ENDIF MSWINDOWS}
  21.   {$IFDEF DARWIN}
  22.   EmpyreanAPIDLL = 'libdbxserverapi.dylib';
  23.   {$linklib libdbxserverapi.dylib}
  24.   libEmpyreanCrypto = 'libEmpyreanCrypto.dylib';
  25.   {$linklib libEmpyreanCrypto.dylib}
  26.   {$ENDIF DARWIN}
  27. ...
  28. ...
  29. type
  30.   TgetStatusThread = class(TThread)
  31.   private
  32.     fStatusText: string;
  33.     eMail: string;
  34.     procedure ShowStatus;
  35.   protected
  36.     procedure Execute; override;
  37.   public
  38.    Constructor Create(CreateSuspended : boolean);
  39. end;
  40. ...
  41. ...
  42. function checkUserStatus(loginName: pChar; out successBoolean: pChar;  out responseMessage: pChar; out errorCode: pChar; out errorMessage: pChar)  : integer;  {$IFDEF MSWINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF} external EmpyreanAPIDLL;
  43. ...
  44. ...
  45.  
  46. constructor TgetStatusThread.Create(CreateSuspended : boolean);
  47. begin
  48.   inherited Create(CreateSuspended);
  49.   FreeOnTerminate := True;
  50. end;
  51. ...
  52. ...
  53. procedure TfEMPServerTest.bCheckUserStatusClick(Sender: TObject);
  54. var
  55.   getStatusThread: TgetStatusThread;
  56.  
  57. begin
  58.   WriteLog('DEBUG', 'in TfEMPServerTest.bCheckUserStatusClick');
  59.   getStatusThread := TgetStatusThread.Create(True);
  60.   WriteLog('DEBUG', 'before start');
  61.   getStatusThread.Email := checkUserStatusEmailEdit.Text;
  62.   getStatusThread.Start;     // START THE THREAD - YES THIS WORKS
  63.   WriteLog('DEBUG', 'after start');
  64.  
  65. end;
  66.  
  67. procedure TgetStatusThread.ShowStatus;
  68. // this method is executed by the mainthread and can therefore access all GUI elements.
  69. begin
  70.   fEMPServerTest.Memo1.Lines.Add('Status Returned: ' + fStatusText);
  71.   showmessage('TgetStatusThread.ShowStatus Status Returned: ' + fStatusText);
  72.   WriteLog('DEBUG', 'Status Returned: ' + fStatusText);
  73. end;
  74.  
  75. procedure TgetStatusThread.Execute;
  76. var
  77.   successBoolean: pChar;
  78.   responseMessage: pChar;
  79.   errorCode: pChar;
  80.   errorMessage: pChar;
  81.   ReturnResult: integer;
  82.   newStatus : string;
  83.  
  84. begin
  85.   Writelog('DEBUG', 'In TgetStatusThread.Execute Thread');   // I DEFINATELY GET HERE AND THE LOG IS WRITTEN TO
  86.   fStatusText := 'TgetStatusThread Starting...';
  87.   Writelog('DEBUG', fStatusText);
  88.   Synchronize(@Showstatus);
  89.   fStatusText := 'TgetStatusThread Running...';
  90.   Writelog('DEBUG', fStatusText);
  91.   Synchronize(@Showstatus);
  92.   responseMessage := 'Nil Response';
  93.   ReturnResult := checkUserStatus(pChar(Email), successBoolean, responseMessage, errorCode, errorMessage);  // THIS IS A CALL TO A FUNCTION IN A DYLIB AND IT HANGS HERE
  94.   WriteLog('DEBUG', 'TgetStatusThread.Execute after checkUserStatus');                                       // NEVER EXECUTED
  95.   fStatusText := 'Return Result: ' + inttostr(ReturnResult) + #13#10 + 'Response: ' + string(responseMessage);
  96.   Synchronize(@Showstatus);
  97.   fStatusText := 'TgetStatusThread Terminating...';
  98.   Synchronize(@Showstatus);
  99.   WriteLog('DEBUG', fStatusText);
  100.  
  101.   while (not Terminated) and (responseMessage = 'Nil Response')do
  102.   begin
  103.     NewStatus := 'TgetStatusThread Update';
  104.     Writelog('DEBUG', NewStatus);
  105.     if NewStatus <> fStatusText then
  106.     begin
  107.       fStatusText := newStatus;
  108.       Writelog('DEBUG', 'TgetStatusThread Loop synchronize');
  109.       Synchronize(@Showstatus);
  110.     end;
  111.   end;
  112.  end;
  113.  

Problem #1:
So the first issue is that Synchronize never executes the ShowStatus method (hence the gazillion Writelog statements so I can see what's happening).

Is this some issue with OSX or is it something else more insidious?

Problem #2:
When I get to the external function (which works fine if I just call it from the main thread), the code simply hangs. To complicate matters, I am trying to have this DYLIB and it's functions used by a C++ developer on macOS. If he calls from the main thread then OK, if he calls the DYLIB function from a secondary thread it crashes his application.

But first things first, does the DYLIB function need anything special to be called from a secondary thread, and what is a likely reason it is causing the call to hang?

If you need any other/further info let me know.

Would I have any better success with one of the other lazarus thread libraries like say WThread?

Thanks,
Kevin

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2107
    • havefunsoft.com
Re: Threading OSX and DYLIBs
« Reply #1 on: June 13, 2019, 03:21:29 pm »
1)
for this particular issue I can definitely recommend to try using Lazarus trunk.
There were a number of changes done on how Synchronize() work.

2)
The crashes from the secondary thread depend on how the function is written.
Typically a pure function like Min() or Max() would work fine.
BUT if a function is using some global objects, then it might crash pretty fast.

A special consideration for macOS. It might require a special NSAutorelease object to be created on the thread. However, those could be created by the later OSX versions
« Last Edit: June 13, 2019, 03:24:21 pm by skalogryz »
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

kevin.black

  • Full Member
  • ***
  • Posts: 122
Re: Threading OSX and DYLIBs
« Reply #2 on: June 14, 2019, 12:51:34 am »
@skalogryz,

1. Thank you. I have installed FPC fixes 3.2.0 and lazarus 2.1.0. I'll junk that and try and install fixes 3.2.0 and lazarus trunk.

2. The function within the DYLIB calls a couple of internal functions (mainly writing to the log) and performs a REST call using some of the TMS Dropbox component classes, the returned JSON is then decoded in another function. In Delphi/Win32 I have used these classes within a DLL called by a thread and there has been no issues that I am aware of. The only other function is one to allocate memory etc to the pChar values returned.  There are, AFAICT, no globals etc etc, but I cannot confirm that in the TMS code.

Regardless, first thing to try is the install of Fixes/trunk. Thanks for the advice.

Cheers,
Kevin


kevin.black

  • Full Member
  • ***
  • Posts: 122
Re: Threading OSX and DYLIBs
« Reply #3 on: June 14, 2019, 04:35:50 am »
So on the basis that it never rains it pours:
  • I have installed FPC 3.2/Lazarus Trunk
  • When I try to build the test application it can no longer find any of the DYLIBS
  • All of the library paths are the same (it drags this in with the lpr file for each project)
Code: Pascal  [Select]
  1. const
  2.   {$IFDEF MSWINDOWS}
  3.   EmpyreanAPIDLL = 'DbXServerAPI.dll';
  4.   libEmpyreanCrypto = 'libEmpyreanCrypto.dll';
  5.   {$ENDIF MSWINDOWS}
  6.   {$IFDEF DARWIN}
  7.  
  8.   EmpyreanAPIDLL = 'libdbxserverapi.dylib';
  9.   {$linklib libdbxserverapi.dylib}
  10.   libEmpyreanCrypto = 'libEmpyreanCrypto.dylib';
  11.   {$linklib libEmpyreanCrypto.dylib}
and these lines are causing the linker error that the libraries are not found (to be safe, I have put the DYLIBs in the app folder, in the app itself, in a library folder and in /usr/local/lib, yet all I get is this library not found error trying to build):

Quote
Messages, Warnings: 17, Hints: 78
...
...
Error: ld: library not found for -ldbxserverapi
An error occurred while linking
Error: Error while linking

So I cannot even tryout the 'fixes' in lazarus trunk until I can sort out the libraries not found (which TBF is seeming a little daunting, I cannot imagine where the linker is looking and why it isn't looking at the folders where the DYLIBs are).

Does anyone know why the DYLIBs are not being found in this frehs install?

Thanks,
Kevin