Jump to content

Dalija Prasnikar

Members
  • Content Count

    1149
  • Joined

  • Last visited

  • Days Won

    106

Everything posted by Dalija Prasnikar

  1. Dalija Prasnikar

    The Case of Delphi Const String Parameters

    I wrote more elaborate post on const parameters https://dalijap.blogspot.com/2021/01/are-const-parameters-dangerous.html
  2. Dalija Prasnikar

    The Case of Delphi Const String Parameters

    This problem is pure ARC problem. One instance of something is destroyed when other one is created and assigned because const parameter did not trigger reference counting mechanism - this is why const is used as speed optimization in the first place. Same thing that happens with strings happens with reference counted objects, but it is easier to see what happens with objects. You need form with memo and button to test following code. IFoo = interface function GetCount: Integer; function GetNumber: Integer; end; TFoo = class(TInterfacedObject, IFoo) private FNumber: Integer; public constructor Create(ANumber: Integer); destructor Destroy; override; function GetCount: Integer; function GetNumber: Integer; end; constructor TFoo.Create(ANumber: Integer); begin inherited Create; FNumber := ANumber; Form1.Memo1.Lines.Add('Created ' + FNumber.ToString); end; destructor TFoo.Destroy; begin Form1.Memo1.Lines.Add('Destroyed ' + FNumber.ToString); inherited; end; function TFoo.GetCount: Integer; begin Result := FRefCount; end; function TFoo.GetNumber: Integer; begin Result := FNumber; end; procedure TForm1.Button1Click(Sender: TObject); var s1: IFoo; procedure Test(const Value: IFoo); begin s1 := TFoo.Create(456); Memo1.Lines.Add('Test Value ' + Value.GetNumber.ToString); end; begin s1 := TFoo.Create(123); Test(s1); end; Output is: Created 123 Created 456 Destroyed 123 Test Value 123 Destroyed 456 It is pretty obvious that object instance holding 123 is destroyed before its value is printed. Value.GetNumber.ToString is accessing dangling pointer - if the memory where object or string previously occupied is not reused, you may get expected value even though that instance is dead, but if memory is reused in the meantime you will read content of some other data. Also the whole thing might crash, or it might not. Basically you get undefined behavior.
  3. Thanks! About the delay... the final release was originally planned for December 11, but I had a minor accident on November 30, and ended up in ER, where I also tested positive for COVID! In less than a week whole family got infected. We all had mild symptoms and were left at home with no treatment., but living was pretty hectic for most of the following two weeks... Anyway, we managed to release it about 16 days later than expected (I already had a lot of it written before, just not fully ready for release) I would say that all this is behind us, as it is... but as you may have heard it... Croatia got hit by major earthquake today. This one was 50km from Zagreb where we're located, but it was 30 times stronger than the one that hit Zagreb in March. After 6.2 by Richter scale, local Nuclear Power Plant (Krsko) was shut down, and power is all but stable, which is why I may not be online as regularly as before in the coming days!
  4. It looks like the download link was not reset properly for the original version buyers. It should work now! If you're still having problems, please contact us through the website form at dalija.prasnikar.info. We may need additional information.
  5. Dalija Prasnikar

    Weak reference is dangerous

    Well, there is really nothing to document because nothing is really thread safe in Delphi. Unless you want every single documentation page having "This is not thread safe" (I am exaggerating a bit, but this is generally true in 99.99%)
  6. Dalija Prasnikar

    Weak reference is dangerous

    You use weak when you cannot guarantee that weak reference lifespan will be less or equal to original strong reference. For instance, let's pretend that TComponent is truly reference counted class, and that same goes for its descendants TEdit and TMenu. Edit has PopupMenu property that can be set to independent TMenu instance. Currently destroying such TMenu instance would leave dangling pointer in Edit PopupMenu property, but we have notification mechanism that notifies Edit that Menu will be destroyed and that Edit should clear reference to the menu. In imagined reference counting scenario, you could have popup menu marked as weak reference and you would not need any notification mechanism to clear that reference when PopupMenu instance is deleted because weak would zero out (clear) that reference when menu is destroyed. If you use unsafe instead of weak, then you would have dangling pointer in PopouMenu property.
  7. Dalija Prasnikar

    Weak reference is dangerous

    No. While unsafe can also be used to break reference cycles, it cannot be used interchangeably with weak, meaning unsafe used in wrong place can lead to dangling pointers. Again, using weak is perfectly fine. If you read my book example again, you will see that you cannot even safely take strong reference from strong reference if original has been written to. So now what? Don't use interfaces at all because they are not thread safe. Don't use strings and dynamic arrays because they are not thread safe?
  8. Dalija Prasnikar

    Weak reference is dangerous

    You are mixing apples and oranges. 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. I will say it again, assignment of any non trivial type in Delphi is not thread safe. Never has been. As soon as one thread is writing some data, all access to that data must be protected. There is no way around it.
  9. 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.
  10. Dalija Prasnikar

    WinUI in Delphi (planned for 10.5?)

    64bit IDE is not question of easiness. It is also question of 3rd party libraries and design packages support. You cannot mix 32bit and 64bit DLLs so having 64bit IDE would mean everyone would need to provide their components and plugins as 64bit versions, too. Also, because you cannot mix 32bit and 64bit DLLs in a process, there is question of other parts of the Delphi IDE - this is not just Editor, but also compiler, linker, debugger... not everything is written in Delphi and porting any of the necessary parts to 64bit may not be straight forward. Easy part of 64bit transition is about Delphi applications, even though depending on your code you may have smooth or bumpy ride, too. I didn't have to do any adjustments for my code, because I never used any hacks around integer - pointers casting and I always had bitness explicitly specified for any data types I have used. So, generally most of transition to 64bit should be easy, but it also depends on specific projects and other libraries, plugins and interactions they might have and use. Scaling is bit different story... VCL Styles originally didn't have High DPI support, and when they were added to IDE theming in Rio they were still without High DPI support... now they have High DPI support, but tweaking the IDE to fully work under High DPI does require some work. This part would be the same with or without VCL Styles. I would first make IDE high DPI aware and then add styles... but that is me... why are some things done the way they are, I have no idea.
  11. Dalija Prasnikar

    Outdated Delphi Roadmap

    Yes. It can have huge impact. If some feature is scheduled for improvement, that means it will be looked at, but the more work feature needs, the more chances is that some parts will not be done right or some accidental bugs might creep in. Also public API can change only at major releases. If some API gets designed in a wrong way, it must stay like that until the next major release. I personally don't use floating point much, so I don't know how broad are required changes to improve performance and whether this will be some compiler tweaking or changes involved require significant API changes, too. In any way, it is always hard to predict different use cases and most important pain points, so having feedback from people that are experts in the area and that know what they need is crucial. Subscription is needed for regular users, but there are also exceptions for cases where people are invited because they can provide valuable feedback in certain areas.
  12. Runtime packages are not always ideal solution, so you have to weight your options. Just for fun... my installer application written in D7 is 1.1MB, same code recompiled in XE4 is 3.5MB, 10.4 4.7 MB. Windows 32bit Of course, no DLLs here... but even if you can argue that there are substantial differences and needed improvements between D7 and XE4, between XE4 and 10.4 when it comes to VCL new features were sparse, and certainly not large enough to justify size difference. I mean the whole darn setup in D7 with several forms fits into that difference (XE4-10.4)
  13. If you have many DLLs with bloat is duplicated. Also code size is not something that should be observed from disk size perspective. How do you deliver that code also matters. If you need to make frequent updates to some remote place with poor or expensive Internet connection then every byte counts. Literally.
  14. Dalija Prasnikar

    Simple inlined function question

    DelphiCon schedule is totally unrelated to any release. It is just online Delphi conference. Previously we had CodeRage in about same timeframe.
  15. There is feature request for expanding helpers functionality https://quality.embarcadero.com/browse/RSP-13340
  16. I agree with specific intent part. Not necessarily with always measuring (but this has tendency to push discussion in wrong direction). Measuring performance bottlenecks is important, but once you know that particular construct can cause bottleneck, you might want to avoid using it in performance critical code, even though you will not exactly measure how much of bottleneck that construct really is in that particular code. In other words, life is too short to measure everything, sometimes you just use what you know is the fastest code (with all downsides considered). I am using Items, when I am iterating over small collections in non-critical code. Yes, exposing internals is not the best practice in general. In case of TList it is necessary sacrifice because performance requires it. Why not something else - well you can use something else, but you are out of option when core RTL/VCL/FMX frameworks are concerned. For instance speed optimization of FMX framework described in https://dalijap.blogspot.com/2018/01/optimizing-arc-with-unsafe-references.html includes direct array access and using List property. With that optimization in just few places I got from "it barely scrolls" to "fast scrolling" on mobile applications. Yes, ARC also had huge impact in this case, but even without ARC compiler, you can still get better GUI performance on mobile apps with such optimization, and on low end devices every millisecond counts.
  17. I think you are blowing this out of proportion. Understanding that accessing underlying array does not have range check error implemented is not nuclear physics. Nor is making sure that you don't screw up your indexes. If you expect some piece of code needs to be performant for any reason (called a lot, or operating on large data sets) you don't have to really measure every two lines of code to know how to make that part of code run faster. Why? I know that TList is not the most brilliant piece of code out there, but it is not that bad either. And iterating through dynamic array (List property) will not be any faster if you use some other collection.
  18. It won't fully help you with accessing TList List property. Since List can have more items than actual number of items stored, you can access index at the end of array that will not trigger compiler range error, but you will be accessing items outside of TList Items range.
  19. Access through Items (GetItem) implements range check and throws exception if index is out of range. Using List gives you direct access to underlying dynamic array (which capacity may be larger than number of items) and there is no range check in place. If range checking does not bother you - because you know that you are looping through correct index range, then you can safely use List.
  20. Some common RTL string manipulation functions have several overloads - including RawByteString one. If you use such function on AnsiString, you will trigger automatic compiler Unicode conversion. Using RawByteString will just use appropriate overload. There might be other differences, I haven't been fiddling around 8-bit strings (writing new code, besides the one I already have) for at least 5 years now. None that I have found so far. I am using RawByteString for mixed ASCII/UTF8/binary data for 10 years now. No problems.
  21. Even AnsiString is back, but you can safely use RawByteString and UTF8String. Original version of NextGen compiler didn't have support for 8-bit strings, but it is not brought back just because of community demands, but because UTF-8 is commonly used Unicode encoding, especially on Linux. So RawByteString and UTF8String were introduced in NextGen as a step toward supporting Linux. Now when NextGen is gone, you can also use AnsiString, but that type is really legacy nowadays and it makes no sense to use it beyond that.
  22. TBytes is preferred type for manipulating binary data. However, it lacks some features and behaviors that string has (copy on write, easy concatenation, string manipulating routines (even if you have to copy paste pieces of RTL code to adjust them for RawByteStrings, this is easier and faster than handling TBytes), and most important one debugging). If those features are needed, then RawByteString is the only other option you have left. Which codepage to use is debatable - probably 437 (I have been using UTF8 because textual data I am extracting from such strings is always in UTF8 format, so this makes handling easier). Most important thing is, you should avoid calling ANY function that does Unicode transformation and usually that means some common Delphi string functions will be out of reach, and you need to make your own. Of course, the whole thing needs to be covered with tests to prevent accidental mistakes when you change pieces of code. With CP 437 you might get away with transformation problems, but any unnecessary conversion also means performance loss. Now I will go sit in my corner waiting to be tarred and feathered.
  23. Dalija Prasnikar

    git workflow question

    merge --squash (if I am not mistaken, never used it as I am always rebasing) Remotes never have feature branches, if they do they are only secondary remotes, not main ones. So, yes, I want that history preserved. Rebase without fast forward does that. I usually don't commit experimental stuff in branches that will end up being merged. And if I do, then it is because such commits contain some approaches that are not applicable at the moment, but I want them preserved for the future because requirements can change or some missing compiler/framework features might be implemented. Sometimes, some cruft will end up in repo, but there is not enough of that to be a problem.
  24. Dalija Prasnikar

    git workflow question

    That part is purely optional. You can do as you please. But when you are merging without rebasing, you also get the whole sausage, unless you squash. Yes, never rebase a public branch is more clearer. That is what was meant with that sentence. It is never call rebase when you are on public branch. In other words when active branch is public branch.
  25. Dalija Prasnikar

    git workflow question

    I found this answer on Stack Overflow about merging branch to master. Maybe it will help. https://stackoverflow.com/a/29048781/4267244
×