Posts

Excessive Collection Locking

As soon as you step into writing multithreaded code, you will need to work with collections in a thread-safe manner. While Delphi provides some basic thread-safe collections for its own use in the RTL and visual frameworks, those are usually not enough. So you will either end up using some third-party thread-safe collection library, or you will be insipired to roll out your own. Making thread-safe collections is not a very complicated task, but if you are not careful, you can easily end up using locking patterns which will look fine at first glance, but can cause excessive locking when used. You will get thread safety, but running at a snail's pace. Considering that modern computers are powerful enough, such code can still run at an acceptable speed, and you might not even notice that you are burning CPU cycles in vain. Thread-safe Collection To implement any thread-safe collection, you will need a lock. While there are many different lock types we can use based on particular n...

Delphi 13.1 Update: Windows Arm64EC compiler

Image
The Delphi 13.1 update has just been released. One of the most significant new features in this update is the new 64-bit Windows ARM EC compiler. With ARM taking a strong foothold in desktop, laptop, and tablet computers, this is an additional Windows platform which can no longer be ignored. Now, if there is one thing that Microsoft does well, it is compatibility support for existing applications. Which means even your existing 32-bit and 64-bit Intel-based Windows applications will run on the Windows ARM OS. With that in mind, having a Windows ARM compiler at this time may not seem like a mission-critical feature, but this compiler still represents an extremely important milestone in Delphi's evolution. There are several reasons for that. First of all, Delphi developers are primarily Windows developers. Even if we support other platforms, Windows is and will always be our turf. Because Windows ARM EC supports both the VCL and FMX visual frameworks, no matter what kind of Delphi...

Using atomic operations to achieve thread safety

Atomic operations are ones which can be executed on otherwise-unprotected shared data as a single indivisible step, where no other thread will be able to compromise the consistency of either that shared data or the operation itself. In other words, even for operation logic consisting of multiple, compound operations—steps—being atomic means that all those steps will run as an unbreakable whole on the shared data, and multiple threads will not be able to interfere with each other. Most fundamental atomic operations are actually implemented on the hardware level, and rely on specific CPU instructions to ensure atomicity, but some can also be implemented in higher level code with the help of those atomic primitives. Because hardware support is needed for the most fundamental primitives, atomic operations will be limited to simple data types commonly up to a size of a pointer. Support for atomic operations for larger data sizes—double pointers or even more—is hardware-dependent, and Del...

CodeRage 2025: Multi-threaded programming: two problems, you now have

Image
 It is a CodeRage season.  Join me next Tuesday, December 2, at 20:00 GMT for my session "Multi-threaded programming: two problems, you now have" and find out how to avoid some common problems when writing multi-threaded code in Delphi applications. Register now!

Delphi 13 Florence: 64-bit IDE debugging experience

Image
Anyone who has ever debugged a 64-bit application in Delphi, especially one that involves multiple threads, has probably experienced disconnected session issues, or the IDE not showing watch values, or simply getting non-responsive in the middle of a debugging session. The new 64-bit IDE and its LLDB-based debugger provide a solution to such problems. You may no longer have to sprinkle your code with logging code that is otherwise not needed; nor will you have to waste hours trying to catch some error, only for Delphi to die on you when you are two lines of code away from discovering the problem. However, it is not all that peachy. Namely, LLDB has its own quirks. First of all, it is dog slow. It is slow when evaluating breakpoints, it is slow when handling exceptions (even ignored ones), it is slow when it stops at a breakpoint and shows local variable values. The more powerful computer you have, the better the experience will be. While there is still plenty of room for improveme...

Universal storage for Delphi procedural types

Delphi's procedural types fall into three categories: standalone procedures and functions, methods, and anonymous methods. The variables that can store those types are all different, both in their memory size and their nature. Further differences between various procedural types come from the number of parameters they have and their types, as well as whether they return some result—whether they are functions. Those differences have an impact on how they are invoked, but not on their storage—the variables and their memory layout. Two procedural types will be compatible with each other, if they are of the same category, have the same calling convention, have the same number of parameters (including the return value or lack of it). and those parameters and return value are of the same types and declared in the same order. The declared parameter names can be different, and don't affect this compatibility. Declaring any of those procedural types has some common parts: a type iden...

Patterns for constructing reference-counted objects: Factory functions vs `as` operator

The construction of reference-counted object instances, which have automatic memory management, is a recurring topic. The main problem with such instances is that they need to be assigned to an interface reference (explicit or implicit) to have a properly initialized reference counting mechanism and a properly managed lifetime. There are many different coding patterns and scenarios around the construction and assignment of such instances. The simplest scenario is having an explicit interface reference to which we will assign the newly constructed object; in some, the compiler will lend us a hand and insert the appropriate code behind the scenes; and in some, it will do the opposite, due to either some compiler bugs or an ambiguity in the code which cannot be automatically resolved by the compiler. In previous blog posts, I have previously covered some such scenarios which needed special attention from developers to avoid bugs: Hello Old New Leaky Friend Inline Variables Type I...

Delphi 12.3 Update

Image
The Delphi 12.3 update has just been released. The most exciting part of this release is the long-awaited 64-bit IDE. Now, this is only an Initial Release of the IDE, and it is not yet a full replacement for the 32-bit IDE and its functionality. The main advantage of the 64-bit IDE is that the available memory is no longer constrained by the 32-bit address space, and the upper limit is now the available system memory. The more you have, the more you can use. This is a great news for all developers with large projects, where the 32-bit IDE would easily hit the memory wall. To start with, the 64-bit IDE only supports the 64-bit Windows platform. The primary purpose of such a release with only partial functionality is to allow all component developers to update their design-time packages, and prepare them to support the 64-bit IDE. And to do that, you only need the 64-bit Windows platform anyway. For all other platforms, there is no immediate advantage of using a 64-bit IDE. As we all...

Stack Overflow: A year in moderation

On the 13th March, last year I was elected as a Stack Overflow moderator. I am using this opportunity to thank everyone who who voted for me back then. It has been an interesting year. The "Chinese curse" kind of interesting, but I can't talk about that too much. The world is flooded with AI slop, and everyone and their dog is using it. And Stack Overflow users are no exception. Low-reputation users, high-reputation users, the magical song of AI is too hard to resist. Literal thousands and thousands of users have posted AI answers, thinking they will get away with it. Thousands and thousands have failed. When someone asks me why am I so skeptical about AI? The answer is really simple: I have seen it all. I know what it can and what it cannot do. And while it is certainly a useful tool when used carefully and for specific purposes, most of the time it is a huge time waster. Responsible use of AI is a rare occurrence on the site. I think I can count the number of case...

Celebrating 30 Years of Delphi With a New Book: Delphi Quality-Driven Development

Image
This year, on February 14th, Delphi celebrates its 30th birthday. Over the past three decades, Delphi has proven to be a robust and versatile development environment, empowering developers to build high-performance applications with ease across multiple platforms: Windows, Linux, Android, iOS, and macOS.  As we commemorate this milestone, I am also introducing a new book to help guide you into Delphi's fourth decade: Delphi Quality-Driven Development. Useful to lone developers and vast teams alike, this book aims to demonstrate a variety of essential practices and techniques for making high-quality, testable code. There is a 25% sale going on until the end of February for the Delphi Quality-Driven Development ebook, and there you can also get an additional discount on other books if you add them to your order. To all my fellow Delphi developers: May your code compile quickly, your memory be manageable, and your code testable.   Delphi Quality-Driven Development Book A practica...

Hello Old New Leaky Friend

Image
Developers that extensively use interfaces in Delphi probably know about a long-standing issue with the compiler, where interface parameters declared as const or [ref] will cause a memory leak if a reference-counted object instance is constructed directly at the call site. Illustration: Sandra Prasnikar        Specifically, the following code would create a leak: procedure LeakTest(const Intf: IInterface); begin end; procedure Run; begin LeakTest(TInterfacedObject.Create); end; begin ReportMemoryLeaksOnShutdown := True; Run; end. The problem arises because the compiler does not create a hidden interface reference that would properly initialize the reference counting mechanism and keep the object instance alive during the call. In other words, such an object would never be assigned to a strong interface reference. Because const and [ref] parameters don't trigger reference counting, if there is no other code within the called routine, the obj...

Delphi 12.1 & New Quality Portal Released

Image
The Delphi 12.1 update has been released. Besides a number of bug fixes, this release also has a few new features. The most notable ones are the new Split Editor, which allows having multiple files opened side by side; and support for Android API 34. You can find more information about what is new in this update at https://docwiki.embarcadero.com/RADStudio/Athens/en/12_Athens_-_Release_1 However, the greatest change for all Delphi developers is a new bug tracking portal that replaces the old one, still available in read-only mode at https://quality.embarcadero.com/ . The new portal is hosted in Atlassian Cloud https://embt.atlassian.net/servicedesk/customer/portals  or through a redirect https://qp.embarcadero.com and is based on Jira Service Management (JSM), while the internal bug tracking system, which was also migrated to the cloud, was not changed and is running on Jira. This move to the cloud was inevitable, because Atlassian retired their Server line of products. JSM i...

Catch Me If You Can - Part II

It has been quite some time since I wrote the Catch Me If You Can post about the inability of LLVM-backed compilers to catch non-call hardware exceptions. In other words, hardware exceptions raised by code written in the same block as the try...except exception handler. Since then, more LLVM-backed compilers have been added to Delphi, and with those, more reasons to change common coding practices... but still, very few developers know about the issue. The original article covered only try...except exception handlers, and exploring what exactly happens with implicit and explicit try...finally handlers was left as an exercise to the readers and to some future, now long overdue, post. And Now: All Hell Breaks Loose When you read about non-call hardware exceptions not being caught by an immediate try...except block, the implications might not look too serious. After all, while plenty of code can raise exceptions, try...except handlers are not that frequently used in places, as mo...