Jump to content

vfbb

Members
  • Content Count

    266
  • Joined

  • Last visited

  • Days Won

    30

Posts posted by vfbb


  1. String works exactly like a bitmap (bitmap handle) from firemonkey.
    When you create a string, through another, s2: = s1, this does not immediately create a copy of the string, but rather increases 1 refcount from a single original string, however when you change the string, internally it will check if the refcount is> 1 , it will create a copy, a new string with refcount 1, to make the changes, and not affect other places that are using that string. This was done to optimize, increase performance, as there are some manipulations of strings that in the end do not change the string, for example s2: = s1.PadLeft (0) will not create a copy, will return the string s1 with 1 more reference .

    The use of const in the parameter is an optimizer as it avoids operations with the refcount. However Marco Cantu warns of a problem generated when using const in the method argument, because if inside it you indirectly change the original string (for example by calling an event within its method) and the refcount of that string is 1 (which possibly will be ), within its method the argument with const will become invalid.


  2. Right, I'm generalizing something that I shouldn't because I only work on multithreaded systems with high availability, and in this scenario, I really won't be able to use Weak.

     

    But at least this limitation should be documented, since crashes generated by this limitation are difficult to diagnose. @loki5100 This can be your problem too, once it generate a generic crash like yours.


  3. 35 minutes ago, Anders Melander said:

    If the weak reference is not thread safe then the the weak-to-strong copy also isn't thread safe.

    As far as I can tell there's no way that [weak] references can be implemented thread safe so I don't see a bug here.

    Yes Anders! There is no way to workaround this, the solution is redesign the code to doesn't depend or use weak attribute.

     

    46 minutes ago, Dalija Prasnikar said:

    Weak references work fine and have their purpose when used in single thread scenario. Their purpose is not achieving some magical thread safety, but breaking reference cycles.

    dalija, that's why Unsafe exists.

     

     

    if you cannot transform WeakToStrong reference safetly, then you should not use Weak, you need to redesign your code to not use Weak, or if you just need to avoid circular reference, use Unsafe.

     

    Don’t use Weak, it is dangerous. This is the title and this is truly, once it is not thread safe.


  4. 44 minutes ago, Dalija Prasnikar said:

    So, weak references in Delphi are not thread safe. Also even strong interface references in Delphi are not thread safe. So there is no bug here, just bad test case. 

    Yes! the weak reference is not thread safe. But if you cannot make a strong reference through the weak reference to check if the strong reference is null, as it is not safe, there is no benefit in having it in Delphi, nothing will differentiate you from Unsafe, except for the poor performance.

     

    1 hour ago, FPiette said:

    @vfbb I had a quick look at your sample and I see a lot of threads accessing common data without synchronization. Are you sure that the issue doesn't come from this concurrent access?

    The stress test was really confusing, but it's thread-safe, the only global variables that threads change are booleans.

    Basically the test is to prove that this is not thread safe:

    image.thumb.png.0109363be1159ca94a1b4b4012bed829.png

     

     

    Another user found this problem and explained it in the comments of marco cantu's blog:
    https://blog.marcocantu.com/blog/2016-april-weak-unsafe-interface-references.html

    image.thumb.png.95d6dd2e1129d0e66a8f092b72de2d0b.png


  5. Weak reference in interfaces is not thread safe and it is dangerous and should be avoided in high availability systems, this is my conclusion after some tests.

     

    It is known that the weak attribute should always be avoided due to loss of performance, giving preference to the unsafe attribute whenever we can guarantee the existence of the interface when using the unsafe reference. On the other hand, Weak should be a powerful resource in programming, serving as a guarantee way to know if an interface still exists or not, because we create a strong reference through the weak reference and check if the strong reference is nil or not and then use it, the strong referenced interface.

     

    It turns out that in the delphi's interfaces internal system, the ARC part is thread safe, but the object's Weak list is not. After the destruction of an interface, the Weak reference list is insecurely traversed setting nil, opening loopholes to use the Weak reference value in some part of the code after the interface is destroyed and before that Weak reference is set to nil. Although this is not clear in System.pas, we can prove it by testing.

     

    Attached is a small stress test project that confirms this issue.

     

    In view of this problem, what is the best alternative for the weak reference?

    TestWeak.zip


  6. 16 hours ago, Dave Novo said:

     

    Aside from breaking the application into different packages/projects, does anyone have any bright ideas.

    This is the correct solution and the Delphi ide/compiler 64 bits is far. 
     

    I faced this problem on a daily basis, the project became too big, and a solution adopted by us was to divide the project into packages, so as not to overload with a single compilation. You can for example divide main project, FMX package and RTL package.

    • Like 1

  7. On 9/27/2020 at 7:41 PM, Dave Nottage said:

    You'll find you won't be able to debug with a device that has iOS 14.0. Deploying will work; just not debugging.

    One more info: in release mode will give error too, but you will see that the app has been installed, just open it manually.


  8. The best strategy should be analyzed for each case, but I can tell you that 99% of the time the implementation of binary tree using a simple ordered list will be much faster than creating objects for nodes, because you need to balance the tree all over moment, while the simulation of the binary tree in a simple ordered list you don't have to balance anything (although the List.Insert or List.Delete can call a very large Move, the CPUs are already optimized for that and it will be much faster that go through node by node balancing the tree to maintain the ideal number of levels of a binary tree which is Log N in base 2, besides the costs of creating the objects of the nodes).


  9. The binary tree is essential in programming, especially when you want to insert, remove, search and maintain an ordered list at the same time, with an excellent performance. I use it, but on a day-to-day basis we don’t use it that much because generally what we need is just a simple sorting, example List.Sort, or a list with a fast search, example TDictionary, or inserting, removing and sorting small lists (for example example lists with less than 200 items).

     

    The ordering of delphi uses QuickSort which has the same complexity / performance as adding + ordering a binary tree O (N * (Log N)). Just like the dictionary has similar performance in the search.

     

    But as I said, when you want to add, remove, search quickly and sort the same list, you better use a binary tree.

     

    Delphi does have support for this in TList<T>, you don't need to create a class or make implementations as alcinoe did, you just need to use for example TList<Integer>, and use List.BinarySearch to add, remove or search. Note: To add you will use BinarySearch because although it returns False, which is what it should return when it cannot be found, it also returns the Index, which is where this item should be, so you will insert the item in the position of that index that BinarySearch returned. That way you will already insert sorted, the list will always be sorted, that is, any operation like this will be very fast, because it uses BinarySearch which has the complexity O (Log N), so your list will be a Binary Tree. 😉

    • Like 1

  10. 2 hours ago, Fintan said:

    10.4 does not appear to be recognising the Browsing Path.

    For instance, in 10.4 I have to add units within $(BDS)\SOURCE\RTL\SYS to the uses clause, whereas in previous versions (coming from 10.2) that was not required.

    Is this a known issue?

    The browsing path works fine. It just not work when the Code Insight is completing in background, then the Ctrl + Click may not work. But while this, you can simple click in the unit name in uses, and press Ctrl + Enter.


  11. I never tested it, but if you open the System.StartUpCopy.pas you will see that in Windows, always when your program run, it will try to copy files from ExtractFilePath(ParamStr(0)) + '\StartUp\' to TPath.GetHomePath (in windows it is AppData\Roaming\).

     

    So, you can try to put in Deployment remote path: .\StartUp\yourapp\database.sqlite3

    And to load the database in runtime you can try to use TPath.GetHomePath + '\yourapp\database.sqlite3'

    • Like 1

  12. One more issue: memory leak using inline variables (just arc types, like strings, arrays and interfaces) + anonymous method. Ex:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      var S: string := 'Test leak';
      TThread.Queue(nil,
        procedure()
        begin
          Showmessage(S);
        end);
    end;

    The report:

    image.png.e6bd00b2aed2cef3b0c18ad39dc5b00d.png


  13. It may not be your problem but I have to inform there is a very common bug with the iOS in 10.3 (not sure in 10.4), that the break points marked is pointing to an wrong line (usually 4~5 lines before or after) from the break point marked by you. It is very common for me to mark within the scope of a method but internally delphi marked on another line that is outside the scope of the method, and when you compile your breakpoint is marked as invalid. In this case I put several breakpoints in sequence, close to the method I want.


  14. @MikeMon I forgot that in Delphi Rio there was a bug in the android permissions and I noticed that it is still in Delphi Sydney. You have to fix the FMX.Platform.Android.pas

     

    procedure TFMXNativeActivityListener.onRequestPermissionsResult(ARequestCode: Integer;
      APermissions: TJavaObjectArray<JString>; AGrantResults: TJavaArray<Integer>);
    // -----------------------------------------------------------------------------
    // [iPub - github.com/viniciusfbb] - 28/05/2020 - Delphi 10.4
    // The delphi messages system isn't thread-safe and this request result can be
    // called in background threads
    {$IFDEF DELPHI_FIXES} // -------------------------------------------------------
    begin
      TThread.Queue(nil,
      procedure()
      var
        MsgData: TPermissionsRequestResultData;
        Msg: TPermissionsRequestResultMessage;
      begin
        MsgData.RequestCode := ARequestCode;
        MsgData.Permissions := APermissions;
        MsgData.GrantResults := AGrantResults;
        Msg := TPermissionsRequestResultMessage.Create(MsgData);
        TMessageManager.DefaultManager.SendMessage(nil, Msg);
      end);
    {$ELSE} // ---------------------------------------------------------------------
    var
      MsgData: TPermissionsRequestResultData;
      Msg: TPermissionsRequestResultMessage;
    begin
      MsgData.RequestCode := ARequestCode;
      MsgData.Permissions := APermissions;
      MsgData.GrantResults := AGrantResults;
      Msg := TPermissionsRequestResultMessage.Create(MsgData);
      TMessageManager.DefaultManager.SendMessage(nil, Msg);
    {$ENDIF} // --------------------------------------------------------------------
    end;

     

    • Like 1

  15. @MikeMon Today I don't have an android device to check it, but first I would check 2 things in andoird:

    1) the Porject Options > Uses Permission > Camera = true.

    2) Test if the "TFormMain.AccessCameraPermissionRequestResult" is executing in the main thread with LIsMainThread := TThread.Current.ThreadID = MainThreadID;

     

    About Delphi 10.3.3 I do not use anymore and the code of iOS that I posted will work only in 10.4 because the iOSapi.AVFoundation.pas of the 10.3.3 not have the apis of media permission.


  16. Mike, the "PermissionsService .RequestPermissions" works only in Android, in others platforms it have not been implemented, will simple skip.

     

    The code I gave you will work perfectly, but I think you are having trouble adapting it. So I simplified things for you:

    {$IFDEF iOS}
    type
      TipPermissionStatusProc = reference to procedure(AStatus: TPermissionStatus);
    
      { TipiOSUtils }
    
      TipiOSUtils = class
      private type
        { IipRequestCameraPermissionDelegate }
    
        IipRequestCameraPermissionDelegate = interface(IipInterface)
          procedure RequestAccessResult(AAuthorized: Boolean);
        end;
    
        { TipRequestCameraPermissionDelegate }
    
        TipRequestCameraPermissionDelegate = class(TipInterfacedObject, IipRequestCameraPermissionDelegate)
        private
          FStatusProc: TipPermissionStatusProc;
        public
          constructor Create(AStatusProc: TipPermissionStatusProc);
          procedure RequestAccessResult(AAuthorized: Boolean);
        end;
      end;
    
    uses
      System.Classes,
      FMX.Dialogs,
      iOSapi.AVFoundation;
    
    { TipiOSUtils.TipRequestCameraPermissionDelegate }
    
    constructor TipiOSUtils.TipRequestCameraPermissionDelegate.Create(
      AStatusProc: TipPermissionStatusProc);
    begin
      inherited Create;
      FStatusProc := AStatusProc;
    end;
    
    procedure TipiOSUtils.TipRequestCameraPermissionDelegate.requestAccessResult(
      AAuthorized: Boolean);
    begin
      if AAuthorized then
        FStatusProc(TPermissionStatus.Granted)
      else
        FStatusProc(TPermissionStatus.Denied);
    end;
    
    { TipiOSUtils }
    
    class procedure TipiOSUtils.RequestAccessResult(AAuthorized: Boolean);
    begin
      TThread.Queue(nil,
        procedure()
        var
          LRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
        begin
          LRequestCameraPermissionDelegate := FRequestCameraPermissionDelegate;
          FRequestCameraPermissionDelegate := nil;
          if Assigned(LRequestCameraPermissionDelegate) then
            LRequestCameraPermissionDelegate.RequestAccessResult(AAuthorized);
        end);
    end;
    
    procedure TipiOSUtils.RequestCameraPermission(AStatusProc: TipPermissionStatusProc);
    begin
      case TAVCaptureDevice.OCClass.authorizationStatusForMediaType(AVMediaTypeVideo) of
        AVAuthorizationStatusNotDetermined:
          begin
            FRequestCameraPermissionDelegate := TipRequestCameraPermissionDelegate.Create(AStatusProc);
            TAVCaptureDevice.OCClass.requestAccessForMediaType(AVMediaTypeVideo, RequestAccessResult);
          end;
        AVAuthorizationStatusDenied: AStatusProc(TPermissionStatus.Denied);
        AVAuthorizationStatusAuthorized: AStatusProc(TPermissionStatus.Granted);
        AVAuthorizationStatusRestricted: AStatusProc(TPermissionStatus.PermanentlyDenied);
      else
        AStatusProc(TPermissionStatus.Denied);
      end;
    end;
    {$ENDIF}

    And to use, replace your TFormMain.FormActivate by this:

    {$IFDEF iOS}
    var 
      LiOSUtils: TipiOSUtils.Create;
    {$ENDIF}
    
    procedure TFormMain.FormActivate(Sender: TObject);
    begin
    {$IFDEF ANDROID}
      FPermissionCamera := JStringToString(TJManifest_permission.JavaClass.CAMERA);
      PermissionsService.RequestPermissions([FPermissionCamera], AccessCameraPermissionRequestResult, DisplayRationale);
    {$ELSEIF defined(iOS)}
      LiOSUtils: TipiOSUtils.Create;
      LiOSUtils.RequestCameraPermission(
        procedure(AStatus: TPermissionStatus)
        begin
          if AStatus = TPermissionStatus.Granted then
            CreateCamera()
          else
            Showmessage('You need to enable the Camera permission in system Settings. Please go to the Settings > MyApp > enable Camera.');
        end);
    {$ENDIF}
    end;

    Note: To test the permission you need to unistall your app before compile.


  17. What I doing? If the status is not determined (first time) I request the access (you can request just if the status is not determined). If is denied I simple show a message informing to go to the Settings and enable the permission manually (all aps do this, example: Telegram). 


  18. Mike, but the ZXing don't ask the permission. You need to do this by your self. I don't know why it is working for you in 10.3, probably the TCamera did it internally.

    I can't send you my entire code, but you will understand how it works by seeing part of the code:

      TipiOSUtils = class(TipFMXUtils, IipiOSUtils)
      private type
        { IipRequestCameraPermissionDelegate }
    
        IipRequestCameraPermissionDelegate = interface(IipInterface)
          procedure RequestAccessResult(AAuthorized: Boolean);
        end;
    
        { TipRequestCameraPermissionDelegate }
    
        TipRequestCameraPermissionDelegate = class(TipInterfacedObject, IipRequestCameraPermissionDelegate)
        private
          FStatusProc: TipPermissionStatusProc;
        public
          constructor Create(AStatusProc: TipPermissionStatusProc);
          procedure RequestAccessResult(AAuthorized: Boolean);
        end;
      {$IFDEF DELPHI_FIXES}
      private class var
        FCurrentAlert: UIAlertController;
        FRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
        class procedure AlertCancel(AAction: UIAlertAction);
        class procedure AlertSettings(AAction: UIAlertAction);
        class procedure RequestAccessResult(AAuthorized: Boolean);
      {$ENDIF}
      
    // ....
    
    {$IFDEF DELPHI_FIXES}
    class procedure TipiOSUtils.AlertCancel(AAction: UIAlertAction);
    var
      LRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
    begin
      LRequestCameraPermissionDelegate := FRequestCameraPermissionDelegate;
      FRequestCameraPermissionDelegate := nil;
      if Assigned(LRequestCameraPermissionDelegate) then
        LRequestCameraPermissionDelegate.RequestAccessResult(False);
    end;
    
    class procedure TipiOSUtils.AlertSettings(AAction: UIAlertAction);
    var
      LRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
    begin
      GiOSUtils.OpenAppSettings;
      LRequestCameraPermissionDelegate := FRequestCameraPermissionDelegate;
      FRequestCameraPermissionDelegate := nil;
      if Assigned(LRequestCameraPermissionDelegate) then
        LRequestCameraPermissionDelegate.RequestAccessResult(False);
    end;
    {$ENDIF}
    
    {$IFDEF DELPHI_FIXES}
    class procedure TipiOSUtils.RequestAccessResult(AAuthorized: Boolean);
    begin
      TipTask.Queue(
        procedure()
        var
          LRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
        begin
          LRequestCameraPermissionDelegate := FRequestCameraPermissionDelegate;
          FRequestCameraPermissionDelegate := nil;
          if Assigned(LRequestCameraPermissionDelegate) then
            LRequestCameraPermissionDelegate.RequestAccessResult(AAuthorized);
        end);
    end;
    {$ENDIF}
    
    procedure TipiOSUtils.RequestCameraPermission(const ADescription: string;
      AStatusProc: TipPermissionStatusProc);
    {$IFDEF DELPHI_FIXES}
    var
      LCancelAction: UIAlertAction;
      LWindow: UIWindow;
    {$ENDIF}
    begin
      if not Assigned(AStatusProc) then
        Exit;
      {$IFDEF DELPHI_FIXES}
      case TAVCaptureDevice.OCClass.authorizationStatusForMediaType(AVMediaTypeVideo) of
        AVAuthorizationStatusNotDetermined:
          begin
            FRequestCameraPermissionDelegate := TipRequestCameraPermissionDelegate.Create(AStatusProc);
            TAVCaptureDevice.OCClass.requestAccessForMediaType(AVMediaTypeVideo, RequestAccessResult);
          end;
        AVAuthorizationStatusDenied:
          begin
            LWindow := TiOSHelper.SharedApplication.keyWindow;
            if (LWindow = nil) or (LWindow.rootViewController = nil) then
            begin
             AStatusProc(TPermissionStatus.Denied);
              Exit;
            end;
            FRequestCameraPermissionDelegate := TipRequestCameraPermissionDelegate.Create(AStatusProc);
            FCurrentAlert := TUIAlertController.Wrap(TUIAlertController.OCClass.alertControllerWithTitle(
              StrToNSStr(FMXLanguage.GetPermissionTitle),
              StrToNSStr(ADescription + #13#10 + FMXLanguage.GetiOSPermissionMessageText),
              UIAlertControllerStyleAlert
            ));
            FCurrentAlert.addAction(TUIAlertAction.Wrap(TUIAlertAction.OCClass.actionWithTitle(StrToNSStr(FMXLanguage.GetPermissionSettingsButtonText), UIAlertActionStyleDefault, AlertSettings)));
            LCancelAction := TUIAlertAction.Wrap(TUIAlertAction.OCClass.actionWithTitle(StrToNSStr(FMXLanguage.GetPermissionCancelButtonText), UIAlertActionStyleCancel, AlertCancel));
            FCurrentAlert.addAction(LCancelAction);
            FCurrentAlert.setPreferredAction(LCancelAction);
            LWindow.rootViewController.presentViewController(FCurrentAlert, True, nil);
          end;
        AVAuthorizationStatusAuthorized: AStatusProc(TPermissionStatus.Granted);
        AVAuthorizationStatusRestricted: AStatusProc(TPermissionStatus.PermanentlyDenied);
      else
        AStatusProc(TPermissionStatus.Denied);
      end;
      {$ELSE}
      AStatusProc(TPermissionStatus.Denied);
      {$ENDIF}
    end;

     


  19. 7 minutes ago, MikeMon said:

    Hi. Thank you for your answer. The code working on Delphi 10.3.3 is crashing on Delphi 10.4. Btw, if the user hasn't given permission to access the camera yet, he/she will be asked to give one. That works quite fine on Delphi 10.3.3.

    Ok, but the permission will be show just at first time. If your user negate the permission once, he will need to give the permission manually at the Settings of the iOS. In iOS go to the Settings > Your App > enable Camera, and try again


  20. Mike, if your notebook/desktop have a webcam test in windows. Your problem on mobile can be related to the permission to have access to the camera and not in relation to the ZXing code. Here I use it only in windows, and with Delphi 10.4 is working fine.

×