Jump to content

havrlisan

Members
  • Content Count

    31
  • Joined

  • Last visited

Community Reputation

3 Neutral

Technical Information

  • Delphi-Version
    Delphi 12 Athens

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  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. 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
  4. 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.
  5. 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.
  6. 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}
  7. 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.
  8. 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.
  9. 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.
  10. 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().
  11. 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.
  12. 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.
  13. havrlisan

    Using inline variables inside loops

    Very good explanation, thank you!
  14. 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.
  15. 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.
×