Jump to content

havrlisan

Members
  • Content Count

    31
  • Joined

  • Last visited

Everything posted by havrlisan

  1. Many people do. I regularly debug my apps on Android and iOS, and less often on OS X. It's not as easy as debugging on Windows, but it's doable and most of the time helpful. I haven't attempted debugging on Linux, so cannot help you much there, although I can say that attaching the debugger may fail even when running the app through the IDE (especially on Android), so I presume attaching the debugger while the app is already running is even more painful.
  2. FYI there is a reported issue on QP regarding the mentioned bug. It's a shame it isn't mentioned anywhere, I accidentally came across it by scrolling through the issues.
  3. Hi. I ran across a deadlock situation in the System.Rtti on the OS X platform. Below is the call stack for both threads that lock each other: Thread 1: Fmx::Forms::TFrame::TFrame(System::Classes::TComponent*) + 299 (FMX.Forms.pas:7524,1 in SampleApp + 8570027) [0x1017c54ab] 1-25 System::Classes::InitInheritedComponent(System::Classes::TComponent*, System::TMetaClass*) + 107 (System.Classes.pas:4859,1 in SampleApp + 1097787) [0x1010a503b] 1-25 System::Classes::BeginGlobalLoading() + 13 (System.Classes.pas:4805,1 in SampleApp + 1096637) [0x1010a4bbd] 1-25 System::Rtti::TRttiContext::KeepContext() + 123 (System.Rtti.pas:5665,1 in SampleApp + 1462715) [0x1010fe1bb] 1-25 System::Rtti::EnsurePoolToken(System::DelphiInterface<System::IInterface>*) + 55 (System.Rtti.pas:5319,1 in SampleApp + 1607015) [0x101121567] 1-25 System::Rtti::EnsurePoolToken(System::DelphiInterface<System::IInterface>*)::DoCreate(void*) + 72 (System.Rtti.pas:5306,1 in SampleApp + 1606392) [0x1011212f8] 1-25 System::Rtti::TPoolToken::TPoolToken() + 197 (System.Rtti.pas:5253,1 in SampleApp + 1605765) [0x101121085] 1-25 System::TMonitor::Enter Thread 2: System::Rtti::TRttiInstanceMethodEx::GetAttributes() + 41 (System.Rtti.pas:6452,1 in SampleApp + 1637625) [0x101128cf9] 1-25 System::Rtti::TRttiObject::GetAttributes() + 86 (System.Rtti.pas:5788,1 in SampleApp + 1415254) [0x1010f2856] 1-25 __stub_in48s__ZN6System4Rtti37LazyLoadAttributes_MakeClosure_ActRec7_0_BodyEv + 27 (SampleApp + 1622027) [0x10112500b] 1-25 System::Rtti::LazyLoadAttributes_MakeClosure_ActRec::_0_Body() + 591 (System.Rtti.pas:5537,1 in SampleApp + 1622639) [0x10112526f] 1-25 System::Rtti::ConstructAttributes(unsigned char*) + 176 (System.Rtti.pas:5496,1 in SampleApp + 1612512) [0x101122ae0] 1-25 System::Rtti::ConstructAttributes(unsigned char*)::ConstructAttribute(void*, unsigned char*&) + 631 (System.Rtti.pas:5476,1 in SampleApp + 1611943) [0x1011228a7] 1-25 System::Rtti::TRttiMethod::Invoke(System::TMetaClass*, System::Rtti::TValue const*, long long) + 234 (System.Rtti.pas:10149,1 in SampleApp + 1437914) [0x1010f80da] 1-25 System::Rtti::TRttiInstanceMethodEx::DispatchInvoke(System::Rtti::TValue const&, System::Rtti::TValue const*, long long) + 3057 (System.Rtti.pas:6620,1 in SampleApp + 1641665) [0x101129cc1] 1-25 System::Rtti::Invoke(void*, System::DynamicArray<System::Rtti::TValue>, System::Typinfo::TCallConv, System::Typinfo::TTypeInfo*, bool, bool) + 183 (System.Rtti.pas:9346,1 in SampleApp + 1634871) [0x101128237] 1-25 System::Rtti::TRttiContext::Create() + 28 (System.Rtti.pas:5577,1 in SampleApp + 1462540) [0x1010fe10c] 1-25 System::Rtti::EnsurePoolToken(System::DelphiInterface<System::IInterface>*) + 55 (System.Rtti.pas:5319,1 in SampleApp + 1607015) [0x101121567] 1-25 System::Rtti::EnsurePoolToken(System::DelphiInterface<System::IInterface>*)::DoCreate(void*) + 29 (System.Rtti.pas:5304,1 in SampleApp + 1606349) [0x1011212cd] 1-25 System::Rtti::TRttiContext::UseContext() + 119 (System.Rtti.pas:5687,1 in SampleApp + 1606663) [0x101121407] 1-25 System::TMonitor::Enter(unsigned int) + 526 (System.pas:20052,1 in SampleApp + 131422) [0x100fb915e] 1-25 The main issue is the order of entering the TMonitor locks, which I extracted here: Thread 1: TMonitor.Enter(GCTokenLock); -- 5663:TRttiContext.KeepContext TMonitor.Enter(PoolLock); -- 5253:TPoolToken.Create Thread 2: TMonitor.Enter(PoolLock); -- 5524:LazyLoadAttributes TMonitor.Enter(GCTokenLock); -- 5687:TRttiContext.UseContext Am I at fault for reproducing these two call stacks in multiple threads (creating a frame in one, calling TRttiMethod.GetAttributes in the other), or is this a bug in the System.Rtti unit? It's worth noting that the GCTokenLock object (and locking) is only present with the USE_MONITOR_FOR_GLOBALCONTEXT compiler directive, otherwise atomic operations are used: {$IF Defined(WIN32) or Defined(WIN64)} {$UNDEF USE_MONITOR_FOR_GLOBALCONTEXT} {$ELSE} {$DEFINE USE_MONITOR_FOR_GLOBALCONTEXT} {$ENDIF}
  4. havrlisan

    Rtti multi-thread deadlock

    You're right, I see it now. Are there any downsides to that approach? And if not, is there a reason Embarcadero did not do that in initialization and finalization sections? Here's the link to the QP issue: https://quality.embarcadero.com/browse/RSP-44207
  5. havrlisan

    Rtti multi-thread deadlock

    I don't think acquiring the context would prevent the above call stacks from happening though: The first stack goes through System.Classes:BeginGlobalLoading, which directly calls EnsurePoolToken and is not avoidable. The second stack loads RTTI attributes with System.Rtti:ConstructAttributes method, which directly calls System.Rtti:Invoke method. In that method, TRttiContext is created right at the start.
  6. havrlisan

    Rtti multi-thread deadlock

    Sorry, I obviously should have pointed out that Thread 1 is the main thread, but it shouldn't matter since the problem is related to Rtti. Update: I'm aware that one solution for this problem is to wait for one thread to finish using Rtti, then let the other continue. That is what I did, but I still want to hear your thoughts on this, and whether this is a bug that should be reported, or not.
  7. Will there be any issues with declaring inline variables inside loops? Here's an example: procedure Sample(const AFixedArray: TSomeFixedArray); begin for var I = 0 to 10 do begin var LInlineVariable := TSomeFixedArray[I]; // do something end end Is it possible that the compiler will allocate more memory than needed, or something in that context? This seems to me like the only way the inline variable feature may produce issues.
  8. havrlisan

    Using inline variables inside loops

    Thanks for your reply, Remy. The code was just a simplified example for demonstration, compiler would not even allow the same variable declaration twice. Come to think of it, such compiler behavior actually does confirm what Dalija said above. This is correct, thanks for clarifying. I did not know this behavior existed in C#, but it is exactly what I wanted to achieve here. Unfortunately, I don't think we'll see such behavior anytime soon in Delphi.
  9. havrlisan

    Using inline variables inside loops

    Sorry for reviving this thread, but I came across another usage issue with inline variables inside a for loop: var LPair: TPair<string, string>; for LPair in FDict do begin var LKey: string := LPair.Key; FList.Add(procedure begin FDict.Remove(LKey); end); end; In this code I expected for LKey variable to stay on the stack as it is used in the anonymous procedure below, however, LKey will always end up with whichever value is in the last loop. Does this mean that declaring a variable inside a loop will only reinitialize it every time at the same address? Edit: to be clear, this is easily solvable by moving the block to a procedure with the key as a parameter. I'm just curious to find out the exact behavior of inline vars.
  10. I've encountered a bug with generics which I believe is a mistake in the compiler. I'll try to be as detailed as possible throughout the code below. I'm only interested in opinions on what is causing this issue in the low-level code, and on possible workarounds that are not entirely different from the implementation I intended to do here. Note that this is a bare minimum of code implementation necessary to reproduce the bug. TL;DR skip to HERE to see where the bug occurs. program GenericsBugConsole; {$APPTYPE CONSOLE} {$R *.res} uses System.Classes, System.SysUtils, System.Generics.Collections; type // Simple child class and interface inheriting from TInterfacedObject. // Contains one property necessary to demonstrate the bug (by invokation). IAncestorObject = interface ['{8B57E255-F48D-4982-B9AF-71A6ABBCDBA4}'] function GetID: TGUID; property ID: TGUID read GetID; end; TAncestorObject = class(TInterfacedObject, IAncestorObject) protected function GetID: TGUID; end; // Generic list that acts as a wrapper for the generic TList<T>. // Implementation is bare minimum for demonstration purpose. IGenericList<T: IInterface> = interface ['{B449EFCE-527D-4062-924E-A0E0421B8A16}'] procedure Add(const AObj: T); function First: T; end; TGenericList<T: IInterface> = class(TInterfacedObject, IGenericList<T>) private FList: TList<T>; protected procedure Add(const AObj: T); function First: T; public constructor Create; destructor Destroy; override; end; // Classes implementation { TAncestorObject } function TAncestorObject.GetID: TGUID; begin // irrelevant Result := TGUID.NewGuid; end; { TGenericList<T> } constructor TGenericList<T>.Create; begin FList := TList<T>.Create; end; destructor TGenericList<T>.Destroy; begin FList.Free; inherited; end; procedure TGenericList<T>.Add(const AObj: T); begin FList.Add(AObj); end; function TGenericList<T>.First: T; begin Result := FList.First; end; begin try var LList: IGenericList<IAncestorObject> := TGenericList<IAncestorObject>.Create; // HERE: // This is the place where the compiler starts acting weird. The call to Supports() passes, // and the debugger correctly shows that TCastedList is "TGenericList<IAncestorObject> as IGenericList<IInterface>". // Calling LCastedList.Add() also passes, TAncestorObject.Create will be casted to IInterface (as per arg type). var LCastedList: IGenericList<IInterface>; if Supports(LList, IGenericList<IInterface>, LCastedList) then LCastedList.Add(TAncestorObject.Create); // LObj is fetched from LList (IGenericList<IAncestorObject>), // and the debugger also shows that correctly: "TAncestorObject as IAncestorObject". var LObj := LList.First; // This is where the exception occurs. Throughout 3 different projects, I've received three different exceptions: // 1) 'c0000005 ACCESS_VIOLATION' -> Private console app, +300000 lines of code // 2) 'access violation at 0x00000001: access of address 0x00000001' -> Test FMX app, bare minimum code with TForm // 3) 'Privileged instruction' -> Not sure where I got this one, and I cannot get it again. Possibly on another test console app. var LID := LObj.ID; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
  11. havrlisan

    Possible compiler bug with generics?

    Come to think of it, a compiler directive for raising errors in such scenarios would be an awesome option. Something like overflow and range checking compiler directives.
  12. havrlisan

    Possible compiler bug with generics?

    You're right, thank you. I did think that was the issue as you've written, but I thought it would be implicitly cast into IAncestorObject when either Added to TList<T> or when retrieved via First().
  13. havrlisan

    Possible compiler bug with generics?

    I forgot to mention a very important information: If I add the object as IAncestorObject to LCastedList (which expects IInterface), the bug does not happen. And if I add the object as IAncestorObject to the LList (which expects IAncestorObject), the bug also does not happen.
  14. havrlisan

    Using inline variables inside loops

    Very good explanation, thank you!
  15. havrlisan

    Using inline variables inside loops

    That's just unnecessarily complicating things. I'd generally declare the variable before the loop (whether it be before begin or as an inline variable, it doesn't matter), I am just wondering if the compiler is able to interpret the variable declaration as it should (?) in loops.
  16. I'm aware that the creation and manipulation of FireMonkey controls are not thread-safe, but what does it take for Embarcadero to fix/reimplement to enable this behavior? I've gathered that the main issues most certainly arise from using the TList class without locks, such as TFmxObject.FChildren and TComponent.FComponents. Besides the properties, there's also the TMessageManager.Default which returns an instance of TFixedMessageManager, which is also not thread-safe (because it also uses TList without any locks). Besides these, what else could be an issue?
  17. You should add the following in your manifest file: android:exported="true" Here's a pretty good explanation for that: https://www.cafonsomota.xyz/android-12-dont-forget-to-set-android-exported-on-yout-activities-services-and-receivers/
  18. When they say they support Android 13, I believe they mean that you can build an application for that version. However, that does not mean they actually have the latest SDK. You can build apps for Android 13 with older SDK versions, such as the one that Embarcadero provides with RAD studio installation, Android SDK 25.2.5. I highly doubt they'll upgrade their SDK anytime soon as that means they'll have to update all the java interfaces written in Delphi (Androidapi units) and add new ones. That's too much work for them, considering they focus on sales rather than fixing and updating the current code. After all, someone must catch the ChatGPT train! 🤮
  19. Here you go https://quality.embarcadero.com/browse/RSP-40714
  20. The Sync edit highlight always seems on top, even though the current selection should be on top. Here's a better screenshot to understand what I'm trying to say: The selected text is = Some text to
  21. This seems to be a similar issue, but starting from 11.3, initializing a code template (ie. for template) will now mark the "fields to fill" with another shade of blue, that overthrows the selection color. Try creating the template, write some text in the fill field, and try selecting it with ctrl+arrows. This is what I mean: As soon as RAD studio lost focus (I switched to my browser), the template fill fields disappeared, and this is left: This is a bug obviously, but did someone find a workaround?
  22. Hi. As the title says, I cannot debug any Delphi FMX application on my Android phone. I'm using the latest Delphi version (Alexandria, Update 1), and my phone is Samsung A52s. When I try to run in debug mode, the app installs, a black screen shows up on the phone, and RAD studio layout transforms to Debug layout. Two outcomes may happen after that: I get the exception Stop(17), which leads me to CPU view and a call stack containing only "clone" and "bionic_clone", The app stays entirely black, and either nothing happens or RAD studio disconnects after a minute of being in the debug mode. On further investigation, I realized that if I set a breakpoint on Application.Initialize, it is never triggered. Also, if I set a breakpoint while the debugger is attached, RAD studio freezes for ~10 seconds, and after it unfreezes the debugger disconnects (while the app is still running on the phone, still as a black screen). Useful info: The app runs completely normal when opened without the debugger. I am able to debug blank apps with Android Studio. I tried using a different SDK version. I have another phone, Samsung A5 (2017) that can be used with the debugger (but it randomly restarts when debugging, hence unusable). A52s uses Android 12, while A5 is Android 8 (Oreo). I also attached logs from logcat, that are filtered for anything that matches with the app name 'com.embarcadero.BetterProgress'. I don't know what other information may be of use, so if I missed something feel free to write it in the comments. Any idea is appreciated! android-log.txt
  23. havrlisan

    Unable to debug blank application on Android 12

    For those who had the same issue, it seems that the 11.3 update fixed it. Sometimes though, I have to first install the app on the phone, then go to Developer options and select that app in the Wait for debugger to connect option.
  24. havrlisan

    Creating FMX controls in a background thread

    If by caveats you're referring to forced canvas locking or unpredictable behavior, then "can be created" doesn't really mean anything. Creating TBitmap creates TBitmapImage, which calls CanvasClass.InitializeBitmap() method that initializes a bitmap handle that is specific to the graphics engine. In Windows case, the class is TD2DBitmapHandle, and wouldn't you know, this is in its constructor: FContextLostId := TMessageManager.DefaultManager.SubscribeToMessage(TContextLostMessage, ContextLostHandler); and it all goes down the drain.
  25. havrlisan

    Creating FMX controls in a background thread

    This is exactly what I was asking for, thank you very much for the explanation. I got some things mixed up in my head and started concluding that such implementation may be beneficial, hence why I asked about it. My initial thought was that TBitmap should be creatable and drawable in a background thread, but that doesn't seem possible because of global Canvas lock, and IIRC it also uses the TMessageManager (and that's where I expanded my opinion to all other components). That seems like a good idea, no?
×