Using TFPHttpClient on multiple threads lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- Frogfather · 2021-09-17 14:13 Hello, I'm having a play around with TFPHttpClient and TThread in order to get a better understanding of how these components work together. I used https://forum.lazarus.freepascal.org/index.php?topic=40773.0 as a starting point although that post is a few years old and I'm aware that we should use opensslsockets instead. So, following the post above I have a class TMyHttpRequest: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type uses  Classes, SysUtils, fphttpclient, opensslsockets;           { TMyHTTPRequest }   TMyHTTPRequest = class(TThread)  private    fApi: String;    fRes: String;  public    procedure Execute; override;  property api: string read fApi write fApi;  property res: string read fRes write fRes;  end; implementation { TMyHTTPRequest } procedure TMyHTTPRequest.Execute;var  Client: TFPHTTPClient;begin  Client := TFPHTTPClient.Create(nil);  writeln(api);  res := Client.Get(api);  writeln(res);  FreeAndNil(Client);end;                         and the idea is that the main program can create multiple instances of these and they'll go off and do their thing in separate threads. Inevitably the calling method is a button click on a form: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);var  MYThread: array[0..2] of TMyHTTPRequest;  i: integer;begin   for i:=1 to 3 do  begin    MYThread[i] := TMyHTTPRequest.Create(True);    MyThread[i].api:='https ://jsonplaceholder.typicode.com/posts/'+inttostr(i);    MYThread[i].FreeOnTerminate := True;    MYThread[i].Start;  end;end; So, the first thread works as expected - the call is made and data is returned correctly, but the second and third result in an EInOutError 'could not initialize openssl library'. If I put sleep(1000) in the loop, giving each thread time to complete before starting the next it works fine. So that suggests that it isn't possible to initialize openssl when it's already in use. So I'm wondering what is the correct way to make multiple https calls on separate threads? Thanks in anticipation. John C ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 14:25 Hey John C, I haven't tried your code, but if you pay attention to the thread you mention, the OP mentions that he solved the issue with: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i InitSSLInterface; Before he instantiates any of the threads. I don't see that in your code. Could that be the solution? Again, I'm sorry if I misspoke, since I haven't tested you code, and I'm basing this reply on the solution that the OP on that post got to. Hope it helps in any way!! Cheers, Gus ---------------------------------------------------------------------- Frogfather · 2021-09-17 21:39 Hi Gus, Thanks for the quick reply. I did try InitSSLInterface without success - same error resulted. Forgot to mention that in my first post. The OP was using the older method without opensslsockets so maybe it worked using that approach. It does seem to be something to do with only one thread being able to be initialize the openssl library at a time but I don't know enough about its inner workings to work out why that is happening. ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 21:48 Hey John, Quote from: Frogfather on September 17, 2021, 11:39:18 pmThanks for the quick reply. You're more than welcome!! Quote from: Frogfather on September 17, 2021, 11:39:18 pmI did try InitSSLInterface without success - same error resulted. Forgot to mention that in my first post. Humm, I thought that would solve the problem, sorry about that! Quote from: Frogfather on September 17, 2021, 11:39:18 pmThe OP was using the older method without opensslsockets so maybe it worked using that approach. It does seem to be something to do with only one thread being able to be initialize the openssl library at a time but I don't know enough about its inner workings to work out why that is happening. Well, this exhaust my knowledge on the matter Sorry I can't be of any further assistance But I do feel that it's a bit odd that the SSL library is not thread safe, or for that matter, is thread aware and messes things up or something. Quite an interesting conundrum, I'll say!! Cheers, Gus ---------------------------------------------------------------------- Frogfather · 2021-09-24 22:31 Hi Gus, From the openssl docs: QuoteOpenSSL can generally be used safely in multi-threaded applications provided that at least two callback functions are set, the locking_function and threadid_func. Note that OpenSSL is not completely thread-safe, and unfortunately not all global resources have the necessary locks. So it looks like it should be possible - I'll do a bit more investigation and see if I get anywhere. ---------------------------------------------------------------------- dbannon · 2021-09-25 00:45 QuoteOpenSSL can generally be used safely in multi-threaded applications provided that at least two callback functions are set, the locking_function and threadid_func. Note that OpenSSL is not completely thread-safe, and unfortunately not all global resources have the necessary locks. Those two sentences appear to be self contradictory.  Its really not enough to have most of something threadsafe. Unless you can determine, reliably, what what calls you can make and be absolutly sure you will not tickle any of the unsafe bits, its not thread safe. Davo ---------------------------------------------------------------------- balazsszekely · 2021-09-25 07:47 @Frogfather Writeln is not thread safe, use the Synchronize or Queue methods to update visual controls in the main thread. In your case, since multiple threads write to console at the same time, you should use critical sections to prevent race condition and/or deadlock. PS: Please do a quick forum search, there are many examples on how to use critical sections. ---------------------------------------------------------------------- Frogfather · 2021-09-25 07:47 Hi Davo, I've just re-read the docs now that I'm slightly more awake and yes, you are of course correct. My knowledge of multi-threaded applications is not great hence my interest in learning. I didn't need this for any project - I was just curious to see if I could do something similar to a js promise in fpc. If you know of any solution to this - either out of box or 3rd party then I'd be most grateful. ---------------------------------------------------------------------- Frogfather · 2021-09-25 08:10 Hi GetMem, Thanks for that - I used the article at https://wiki.freepascal.org/Multithreaded_Application_Tutorial as a starting point and it uses writeln hence my use of it. As mentioned elsewhere this is just out of interest. I used Object Pascal (Delphi/Kylix) for many years before I was a professional developer and it's interesting to revisit it with slightly more experienced eyes! ---------------------------------------------------------------------- PascalDragon · 2021-09-27 06:55 Quote from: GetMem on September 25, 2021, 09:47:14 amWriteln is not thread safe, use the Synchronize or Queue methods to update visual controls in the main thread. In your case, since multiple threads write to console at the same time, you should use critical sections to prevent race condition and/or deadlock. WriteLn to the console (or even a file) is thread safe in the sense that there won't be any deadlock or race condition. What can happen however is garbled output if one thread is interrupted in the middle of flushing its buffer with another thread then writing.