Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 06/28/22 in all areas

  1. Hm, wasn't the point of the first post in this thread that FreeAndNil isn't big enough of a subject to make a whole video about? Two pages of discussion say otherwise.
  2. I recently spent an entire week trying to fix a bug where in heavily threaded situations customers were seeing random av's. The av's and the stack traces were very random, which made it very difficult to pin down. Debugging threads in delphi is painful at best - any changes to timing can completely mask bugs so using breakpoints was not going to work (and the bug would occur in around 4 out of 40 identical threads). After spending days adding even more (pointless as it turned out) locks all over the show I wasnt' much closer to figuring it out. I suspected it could be a reference counting issue (since I use interfaces a lot), so started sprinkling some FreeAndNils around the code (in destructors) - suddenly those random av's turned into nil pointer exceptions that were much less random. That confirmed to me that the issue was with accessing objects already free'd. The problem turned out to be that I was using weak references where I really needed strong references - a 70hr week of frustration turned into a 4 line change 🤦‍♂️ I really don't understand all the angst and debate about FreeAndNil - use it where it makes sense, or don't - just like any other RTL function or language feature.
  3. Zero, I know it's not thread safe. It wasn't just freeandnil that solved the issue (if only). Just another tool in the box, which I used then removed. I still don't get why people get upset about FreeAndNil - I have much bigger and more important issues with delphi to deal with (like actual bugs in the ide/compiler/rtl/vcl) rather than worry about something so minor.
  4. Wrong solution. Right solution is to learn what you don't know how to do. No shortcut. Certainly not by just saying "use interfaces".
  5. As you say, that's a problem with the developer rather than the function itself. I personally use FAN everywhere and my debug builds use FastMM with all bells and whistles. There aren't downsides to using FAN and it does make it easier to catch some defects.
  6. I was kidding. I don't even have to look at your code to know it is not thread-safe Like I said, using it for debugging purposes when something is off, and for some reason you cannot pinpoint the problem with other tools is fine. Problem is not in using FreeAndNil. It has its use case (beyond debugging purposes). The problem is using it everywhere, and then it obscures the intent of the code. When you FreeAndNil, your code is sending the different message that when you just Free. If you FreeAndNil that means such reference can be nil at any point and all code using such reference also needs to deal with scenario where reference is nil, either constructing new instance if the reference is nil, or skipping operations on nil reference. And that is drastically different code than one dealing with reference that cannot be nil.
  7. Using FastMM with the setting to mark freed memory with a certain pattern ($80808080 by default iirc) would have revealed that issue within a few minutes fwiw.
  8. I gave my reasons and arguments. I never said anyone has to agree with me. One thing is for sure. With ARC compiler that wouldn't have to care about compatibility with existing codebases there would be no Free nor FreeAndNil.
  9. Your argument about intent is the closest that I have ever seen to a cogent argument on this subject. But I still don't buy it. In your argument, if you see code that sets references to nil then that indicates this "optional lifetime" pattern, for the sake of inventing a name. And your point is that if you set the reference to nil every time you destroy an instance, then you can't distinguish the optional lifetime pattern from the two more common lifetime patterns, let's call them "owned lifetime" where the object is destroyed in its owner's destructor, and "local variable lifetime" where the object is created and destroyed in a local method, and the reference is a local variable. The thing is though, when you see FAN in owned lifetime and local variable lifetime, it's unmistakeable what the lifetime is. You never think, oh, I wonder if this might be optional lifetime. That's why I don't buy the argument, and why I've never experienced the issues you describe. What I have experienced though is the pain of using a stale reference. That pain is eased if the reference is nil. And yes, we can use debug MM to write over memory when an object is destroyed, but I don't want that to happen in my release builds. And sometimes bugs only emerge in release builds.
  10. I am not that smart... I need all the help I can get... I cannot say for sure how would it work for my own code, which I know the best, because I don't use FreeAndNil everywhere. But this has definitely been a problem for me when reading other people's code. Or maybe that is just because it was overall not the best code ever written...
  11. You can also press "Like" on the post to increase my rep 🙂 Addition: as you develop generic wrapper, adding record's size check would save you from mysterious bugs in the future.
  12. I don't realize the problem at all. FaN pro's - it could help find bugs and is useful for lazy init. Con's - procedure use instead of a method? Duh. Insignificant for me. I see no reasons to not use FaN everywhere,
  13. I did use that, and it got me closer, but not close enough.
  14. corneliusdavid

    Confusing marketing email from EMB

    Yeah, I get lots of emails like this, too, and just have to delete them. Those seconds glancing at emails add up each day (not being sarcastic). However, I can see Embarcadero wanting to send this out to everyone just on the off chance that: You have one product (Delphi or C++Builder) and have been thinking of getting the other one, or.. You might want to upgrade your current subscription, or ... You know of someone that is ready to purchase but hasn't done it yet and they're not on the mailing list. A couple of years ago, I was a current subscriber of the Professional edition and I took advantage of one of these offers to upgrade to the Enterprise edition. It was considered a new purchase because I did not currently have the Enterprise edition so I qualified for the advertised sale but I was able to get an additional discount because I upgraded from the Professional edition. So in some cases, these sales can even be good for existing customers.
  15. corneliusdavid

    TChromeTabs for Delphi 11?

    It's still in Delphi 10.4 GetIt but since Delphi 11 support has not yet been added, it's not in the D11 GetIt. You can add it yourself quite easily, though: just clone the GitHub project to your development area, copy the Delphi 10.4 packages to a new folder, and open them in Delphi 11. Then you can save the updated projects to your own .groupproj file. I had no trouble compiling/installing them in D11.1.
  16. Okay I dove a little bit deeper (never touched RTTI before). It seems strictly tied to types so free pointer casting won't help. However I likely managed to get correct results program foo; {$APPTYPE CONSOLE} uses System.SysUtils, System.Classes, System.rtti, System.TypInfo; type // enlarge record to be always passed by ref TDateRec = record Year: Integer; Dummy: Double; end; TExample = class public function Calc(const dateRec: TDateRec): Integer; end; function TExample.Calc(const dateRec: TDateRec): Integer; begin result := dateRec.Year + round(daterec.Dummy); // use 2nd field end; var ex: TExample; dateRec: TDateRec; intResult: Integer; function executeInstanceMethod(Reference: NativeUInt; const AName: string; const Args: array of TValue): TValue; var context: TRttiContext; instType: TRttiInstanceType; params: TArray<TRttiParameter>; obj: TObject; Arg: TValue; p: Pointer; begin context := TRttiContext.Create; try try obj := TObject(Reference); instType := (context.GetType(obj.ClassType) as TRttiInstanceType); // get the actual pointer - didn't find another way to TValue.GetAsPointer Args[0].ExtractRawData(@p); // learn method's parameters params := instType.GetMethod(AName).GetParameters; // make new TValue with proper type TValue.Make(p, params[0].ParamType.Handle, Arg); result := instType.GetMethod(AName).Invoke(obj, [Arg]); except on E: Exception do writeln(E.ClassName + ' error raised, with message : ' + E.Message); end; finally context.Free; end; end; function executeInstanceMethodReturnIntArgsStruct(Reference: NativeUInt; const AName: PAnsiChar; Reference2: Pointer): Integer; begin result := executeInstanceMethod(Reference, string(AName), [Reference2]) .AsInteger; end; begin try ex := TExample.Create; dateRec.Year := 2022; dateRec.Dummy := 1; writeln( executeInstanceMethodReturnIntArgsStruct(NativeUInt(ex), 'Calc', @dateRec) ); except on E: Exception do writeln(E.ClassName, ': ', E.Message); end; Readln; end. Works on my XE2, prints "2023"
  17. Unemployably expensive? 🤪
  18. Dalija Prasnikar

    Weak reference is dangerous

    Assignment operation in Delphi are not thread safe. If you have one thread writing and other threads reading, such access must be protected or you will get crashes or buggy behavior. You can safely write and read 32bit aligned simple types, in terms that such code will not cause any crashing, but such code will also not work as intended, unless you don't care about actual values stored. 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. Excerpt from my book Delphi Event-based and Asynchronous Programming (Part 5. Thread Safety) If you have multiple threads accessing - reading and writing - the same reference, one thread can easily interfere with the other. Let's say we have a shared interface reference `Data`, pointing to a previously created object, one thread that sets that reference to nil, and another thread that tries to take another strong reference from that one with an assignment to another variable `Tmp`. The first thread will execute the `_IntfClear` function that will result in object destruction. The second thread, trying to grab a strong reference preventing object destruction, will execute the `_IntfCopy` function: var Data, Tmp: IInterface; Data := nil; // Thread 1 -> _IntfClear Tmp := Data; // Thread 2 -> _IntfCopy If the `Source._AddRef` line from the `_IntfCopy` function executes before the call to `IInterface(P)._Release` manages to decrease the reference count to zero and subsequently calls the object's destructor, all is good. The second thread will successfully capture another strong reference to the object instance. However, the first thread can interrupt the second thread at the wrong moment, just after the `Source <> nil` check was successfully passed, but before `Source._AddRef` had the chance to increment the object's reference count. In that case, the first thread will happily destroy the object instance while the second thread will happily grab a strong reference to an already nuked object, and you can forget about happily ever after. Note: Besides interface references to objects, the reference counting mechanism is also used for other types like strings and dynamic arrays. Assignment of those types is also not thread-safe.
×