1

I created a Android local service in delphi that triggers a thread that creates a Tidhhp, if I stop the service through the command JavaService.stopSelf and start the service again without closing the app, a "Segmentation fault(11)" error occurs when the tidhttp component executes a "get". if I close and reopen the app this error does not occur. My code:

LThread := TThread.CreateAnonymousThread(
    procedure
    var
      pagina: string;
      pegar: tidhttp;
      seguro: TIdSSLIOHandlerSocketOpenSSL;
      compressor: TIdCompressorZLib;
    begin

        try
          pegar := tidhttp.create(nil);
          compressor := TIdCompressorZLib.create(nil);
          seguro := TIdSSLIOHandlerSocketOpenSSL.create(nil);
          pegar.Request.useragent :=
            'Mozilla/5.0 (Linux; Android 7.0; PLUS Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Mobile Safari/537.36';
          pegar.ReadTimeout := 60000;
          pegar.ConnectTimeout := 60000;
          pegar.HTTPOptions := [hoForceEncodeParams];
          pegar.IOHandler := seguro;
          pegar.compressor := compressor;
          pegar.HandleRedirects := false;
          pegar.Request.Accept :=
            'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
          pegar.Request.AcceptLanguage := 'pt-BR,en-US;q=0.8,pt;q=0.5,en;q=0.3';        
          pegar.Request.ContentType := 'application/json';
          pegar.Request.Connection := 'keep-alive';

          pegar.Request.ContentType := '';
          pagina := pegar.Get
            ('https://meyserver.com/api/versions/count');

        finally
          compressor.Free;
          seguro.Free;
          pegar.Free;
        end;
    end);
  LThread.FreeOnTerminate := true;
  LThread.Start;
end;

The delhi debug shows that the error occurs in the "System.Generics.Collections" unit in the function:

function TThreadList <T> .LockList: TList <T>;
begin
   TMonitor.Enter (FLock);
   Result: = FList;
end;

Some idea?

Update:

The command I use to start the service from host app is:

procedure Tfrm_principal.Button13Click(Sender: TObject);
var
  LIntent: JIntent;
begin
  LIntent := TJIntent.create;
  LIntent.setClassName(TAndroidHelper.Activity.getBaseContext,
    TAndroidHelper.StringToJString('com.embarcadero.services.MyTeste'));
  LIntent.setAction(StringToJString('IniciaIntent'));
  TAndroidHelper.Activity.startService(LIntent);
end;

Update 2:

My code in host app:

procedure Tfrm_principal.Button13Click(Sender: TObject);
var
  LIntent: JIntent;
begin
  LIntent := TJIntent.create;
  LIntent.setClassName(TAndroidHelper.Activity.getBaseContext,
    TAndroidHelper.StringToJString('com.embarcadero.services.MyAppTest'));
  LIntent.setAction(StringToJString('StartIntent'));
  TAndroidHelper.Activity.startService(LIntent);
end;


procedure Tfrm_principal.Button16Click(Sender: TObject);
var
  LIntent: JIntent;
begin
  LIntent := TJIntent.create;
  LIntent.setClassName(TAndroidHelper.Activity.getBaseContext,
    TAndroidHelper.StringToJString('com.embarcadero.services.MyAppTest'));
  LIntent.setAction(StringToJString('StopIntent'));
  TAndroidHelper.Activity.startService(LIntent);
end;

My code in service app:

function TDM.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
begin
  if Assigned(Intent) then
  begin
    if Intent.getAction.equalsIgnoreCase(StringToJString('StopIntent')) = true
    then
    begin
      Result := TJService.JavaClass.START_NOT_STICKY;
      JavaService.stopSelf;
    end
    else if Intent.getAction.equalsIgnoreCase(StringToJString('StartIntent')) = true
    then
    begin
      Result := TJService.JavaClass.START_STICKY;
      start_thread;
    end;
  end;
end;



procedure TDM.start_thread;
begin
   TThread.CreateAnonymousThread(
    procedure
    var
      pagina: string;
      pegar: tidhttp;
      seguro: TIdSSLIOHandlerSocketOpenSSL;
      compressor: TIdCompressorZLib;
    begin
          pegar := tidhttp.create(nil);
          compressor := TIdCompressorZLib.create(nil);
          seguro := TIdSSLIOHandlerSocketOpenSSL.create(nil);
        try
          pegar.Request.useragent :=
            'Mozilla/5.0 (Linux; Android 7.0; PLUS Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Mobile Safari/537.36';
          pegar.ReadTimeout := 60000;
          pegar.ConnectTimeout := 60000;
          pegar.HTTPOptions := [hoForceEncodeParams];
          pegar.IOHandler := seguro;
          pegar.compressor := compressor;
          pegar.HandleRedirects := false;
          pegar.Request.Accept :=
            'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
          pegar.Request.AcceptLanguage := 'pt-BR,en-US;q=0.8,pt;q=0.5,en;q=0.3';
          pegar.Request.ContentType := 'application/json';
          pegar.Request.Connection := 'keep-alive';
          try
            pagina := pegar.Get('https://myserver.com/api/versions/count');
          except
             enviar_log('Connection error');
          end;
        finally
          compressor.Free;
          seguro.Free;
          pegar.Free;
        end;
        enviar_log('Exit');
    end).Start;
end;
  • You're not trying to access `LThread` after `LThread.Start;`, are you? FWIW, `FreeOnTerminate` is unnecessary when using `CreateAnonymousThread`, and there's no need to have a reference to it at all. It defeats the purpose of `CreateAnonymousThread`. Instead, just do `TThread.CreateAnonymousThread(...).Start;` without obtaining any reference to it. Idk if this is your issue, but that stands out to me. – Jerry Dodge Jul 19 '19 at 13:41
  • On another note, your `try..finally` block is setup wrong. You should be creating those objects *before* `try`, not after. – Jerry Dodge Jul 19 '19 at 13:44
  • What do you do with `pagina` after getting it? This is only available via this local procedure, and I see nothing about actually making use of what you got from the server. This suggests you have other related code which we cannot see. – Jerry Dodge Jul 19 '19 at 13:46
  • Hi guys,this code is a test only, the thread works correctly and I run the JavaService.stopSelf command after the thread finishes, the service is terminated correctly without any errors, however when I restart the service without closing the app the SF(11) error occurs. – Alexandre R. Pires Jul 19 '19 at 13:56
  • Long story short, I don't see *any* reason why you should be getting the error you're getting except for possibly the few things I've pointed out. As it stands, I don't believe you've shown us the real code. I can understand not wanting to give the real URL, but maybe you're doing something here which you decided to hide from us? – Jerry Dodge Jul 19 '19 at 13:59
  • @JerryDodge This is the whole code because I'm just doing some tests with tidhttp and services on android before producing something I put a log in the destroy event of the service and it shows that the service is being destroyed, but when I run the service again with TAndroidHelper.Activity.startService(LIntent); command this error occurs. – Alexandre R. Pires Jul 19 '19 at 14:08
  • Then there must be a bug in `TIdHTTP`, which I highly doubt. And if this *is* your whole code, then I still don't see what you're doing with the `pagina` result you get from the server. – Jerry Dodge Jul 19 '19 at 14:10
  • @JerryDodge "pagina" is just a variable to get the "idhttp.get" result, as I said this code is just a test to see if it works before developing my app. – Alexandre R. Pires Jul 19 '19 at 14:15
  • How do you know it works if you do nothing with the result? Are you stepping in debug mode when this error occurs? Because the simple fact that you're debugging a thread on an Android device could be the reason why you're getting this error. What happens if you're not in debug mode? – Jerry Dodge Jul 19 '19 at 17:40
  • @JerryDodge I put an try except between idhttp.get and compiled in release mode, when I stop the service and restart from host application the exception is triggered. This only occurs if I stop the service with JavaService.stopSelf, if I run the thread several times, without stopping the service before, it works perfectly. – Alexandre R. Pires Jul 19 '19 at 18:01
  • If I stop the service with JavaService.stopSelf and do not close the host app and start the service again from host app, the error occurs, but if I restart the host app the service starts normally and error does not occur. – Alexandre R. Pires Jul 19 '19 at 18:08
  • @JerryDodge take a look update 2, if I click on Button13, works, but if I click on Button16 and then in Button13 again then "Connection error" occurs. – Alexandre R. Pires Jul 19 '19 at 18:31
  • 1
    @AlexandreR.Pires Since you are able to see the segfault in the debugger, what does the call stack leading up to the segfault look like? – Remy Lebeau Jul 19 '19 at 22:37
  • @RemyLebeau I do not know if I understand what you mean, but debugger show me "Access violation at address BB5FA8FC, acessing adress 0000000D" and stop in "TMonitor.Enter(FLock);" line of "function TThreadList.LockList: TList;" in "System.Generics.Collections" unit. – Alexandre R. Pires Jul 20 '19 at 09:42
  • @AlexandreR.Pires do you not know what a call stack is? The list of current function calls leading up to the current instruction being executed. The debugger has a section that shows the call stack for the thread that threw the exception. In any case, an AV near address 0 usually means a nil pointer was accessed. In this case, `LockList()` was likely called using a nil `TThreadList` object pointer. Looking at the call stack would help narrow down where that list object is being accessed – Remy Lebeau Jul 20 '19 at 17:15
  • You could also disable "Use Debug DCUs" in the project settings, so your debugger doesn't step into the system units, and breaks only in your code. Just because it breaks inside a system unit doesn't mean the system is at fault. – Jerry Dodge Jul 21 '19 at 13:53
  • @RemyLebeau Call Stack window show me: Idsslopenssl.SslLockingCallback(9,1,0xbed47400 'err.c',360) and in code editor show me the line "LList := CallbackLockList.LockList;" in procedure SslLockingCallback(mode, n: TIdC_INT; Afile: PIdAnsiChar; line: TIdC_INT)cdecl; (unit IdSSLOpenSSL) – Alexandre R. Pires Jul 21 '19 at 15:16
  • @RemyLebeau I did a test here, if I do not create the TIdSSLIOHandlerSocketOpenSSL object (seguro := TIdSSLIOHandlerSocketOpenSSL.create(nil);) and use "http" instead of "https" this error does not occur. The strange thing is that the error only occurs if I stop the service with "JavaService.stopSelf" otherwise "https" works perfectly. – Alexandre R. Pires Jul 21 '19 at 15:49
  • @RemyLebeau I did another test, if I create the object "seguro : = TIdSSLIOHandlerSocketOpenSSL.create (nil)" but do not set the property "TIdhttp.IOHandler" the error does not occur. – Alexandre R. Pires Jul 21 '19 at 16:14
  • @AlexandreR.Pires the error is happening inside an OpenSSL callback, so of course the error does not occur if you do not make HTTPS requests. The errors suggests `CallbackLockList` is nil, which it should not be unless `UnLoadOpenSSLLibrary()` has been called. You did say the error only happens during shutdown, so clearly something is triggering Indy to unload OpenSSL from memory. I suggest you put a breakpoint in `IdSSLOpenSSL.UnLoadOpenSSLLibrary()`, then call `JavaService.stopSelf()`, and then look at the call stack when the breakpoint is hit. – Remy Lebeau Jul 21 '19 at 17:29
  • @AlexandreR.Pires also, make sure nothing in your project is trying to invoke HTTPS after `JavaService.stopSelf()` is called. You may need to manually reload OpenSSL after restarting the service, by calling `IdSSLOpenSSL.LoadOpenSSLLibrary()` so Indy can reinitialize everything it needs again – Remy Lebeau Jul 21 '19 at 17:32
  • @RemyLebeau progress here, a just put IdSSLOpenSSL.UnLoadOpenSSLLibrary() before JavaService.stopSelf() and everything is working now, thanks for your help. – Alexandre R. Pires Jul 22 '19 at 00:24

0 Answers0