Jump to content
SimonB

Correctly use TSslHttpRest inside a thread

Recommended Posts

I am hoping someone can give me a few pointers on the correct way to use TSslHttpRest.RestRequest inside a thread.

 

I am using ICS's TSslHttpRest (v9.5 / Delphi 12.3) inside a thread to talk to a REST API, the program has a few threads running at the same time that gather data in the background threads then post it off to diffrent APIs.

To do this I am using RestRequest with the AsyncReq param set to False. 

 

http.RestRequest(httpPOST, 'https://sample.com', { * async * } False, param);

 

This works 99.999% of the time and eveything is happy but on one users PC the threads will lockup about once a week or so, and I tracked it down to ICS, or more likely how I am using it.

The DNS lookup is failing or timing out (not an ICS issue, its a hardware issue) and this is hanging ICS's DNS Lookup applicaton wide, not just the thread it fails in. Leaving them at the httpDnsLookup state. The tread that hangs goes to httpAborting inside the RestRequest.

 

Here is a minimum example of how I am using TSslHttpRest, (actual code is inside a TThread)
 

    TThread.CreateAnonymousThread(
        procedure
        var
            http: TSslHttpRest;
            threadId: Cardinal;
        begin
            http := TSslHttpRest.Create(nil);
            try
                http.MultiThreaded := True;
                http.RestRequest(httpGET, 'https://jsonplaceholder.typicode.com/todos', False);
				//http.MessageLoop; 
            finally
                http.Free;
            end;

            // Log the thread finishing
            threadId := GetCurrentThreadId;
            TThread.Synchronize(nil,
                procedure
                begin
                    Memo1.Lines.Add(Format('Thread  %d completed', [threadId]));
                end);
        end).Start;

 

In the code for TWSocket it says

Quote

    If you have to use multithreading, you have two possibilities:
    1) Create your TWSocket from your thread's Execute method
    ...
    In both cases, you must set MultiThreaded property to TRUE.
    ...
    For both methods to work, you MUST have a message loop withing your thread.
    ... 

   For your convenience,  TWSocket has his own MessageLoop procedure. You can use it from your thread.

 

But if I call TSslHttpRest.MessageLoop after TSslHttpRest.RestRequest it never returns.

If I dont call it it all works are expected for the 99.999% of the time.
But if I don't call it the call to TSslHttpRest.Free can hang when there has been a DNS issue. 

Due to TIcsAsyncDnsLookupThread.Execute calling SendMessage and waiting for the TSslHttpRest control to process it but without MessageLoop it never will.
The call to TSslHttpRest.Free then tries to free its TIcsAsyncDnsLookupThread using UnRegisterIcsAsyncDnsLookup, but that theads is waiting for a responce form SendMessage so never finishes. and GThreadLocalStore is now locked so other threads can not use it.


How can I use TSslHttpRest.MessageLoop and have it return so I can safely free the TSslHttpRest object?

 

 

 

Share this post


Link to post

The only ICS component that uses components within a thread is TMailQuThread in OverbyteIcsMailQueue.pas.  It illustrates all the techniques needed to run within a thread. 

 

Essentially you are currently relying on synchronous mode for TSslHttpRest which has a message loop and a defined timeout, with some error handling.  

 

You should instead add events within your thread, use async mode (last parameter in RestRequest true), and then have your own message loop with FIcsWndControl.ProcessMessages after the request, in which you can watch the request state and cancel if DNS takes too long.  

 

Angus

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×