Repository navigation
FreePascal pitfalls
YadeWira edited this page Nov 7, 2026
·
3 revisions
Rules learned while writing PJA, all measured and none obvious. The first two are in pas/pja.inc.
-
{$R+}does not check pointer accesses.p[10]withp: PBytereads out of range without any error. Validation works only on arrays. -
With
{$Q+},for i := 0 to n - 1with unsignednandn = 0raisesEIntOverflow. Lengths and indices are signed, and loops useHigh(). -
On i386,
Int64is not ordinal: it cannot be aforvariable, and the error only shows up when compiling for 32 bits. Counters inSizeInt, and byte positions inInt64. -
for s in ['short', 'much_longer']truncates the literals to the length of the first one, and with numbers it builds a set of bytes. Iterate over a declared constant array instead. -
On Unix,
TFileStream.Create(f, fmOpenRead)takes an exclusiveflock. A second open of the same file, even in the same process, fails withEAGAIN. For reading:fmOpenRead or fmShareDenyNone. -
FPC does not warn about an unset
Resultif some path doesExit(value).pjafs.Parentreturned whatever was left in the register, and it showed up as an intermittent failure (1 run in ~10).pas/results.pystops the build. -
Pascal is case-insensitive. A variable
versionshadowed the constantVERSIONand no future version was rejected; a variableplainshadowed the functionPlain. The compiler does not warn in either case (the second was caught only because the types did not match).pas/collisions.pystops the build. -
On 64 bits, FPC multiplies two
Cardinals in 64 bits. A hash meant to wrap at 32 bits kept its high bits until the product was wrapped inCardinal(). - A global written from several threads costs more than it seems. The cost accounting wrote one per coefficient from every band thread; the cache line bouncing between cores made each band three times slower.
-
In a DLL, FPC 3.2.2's heap can delete its own lock. It counts the
threads using it, the one that loaded the DLL first, and deletes
heap_lockwhen the count comes back to zero. 7-Zip's file manager loads codecs from a short-lived thread: once it ended, the next block freed by a thread other than its owner (every new thread frees one) entered a deleted critical section, and 7zFM crashed testing a PJA archive (up to 0.6.1). Since 0.6.2 the Windows libraries use the process heap (pas/pjaheap.pas, first in their uses). -
TThread.WaitFornaps 100 ms at a time on Unix when called from the main thread. Short files on several threads were up to 7 times slower;pas/pjathread.pasjoins threads through an event. -
System.ProcessorCountis 1 on Unix in FPC 3.2.2:pas/pjacpu.pascounts the processors itself. -
The signal handler's type is
SigActionHandler.SigActionHandler_texists on Linux only, and the macOS build failed on it (0.9.5). -
A
threadvaris per thread, also in a library. A plugin hook that counts in threadvars gives wrong totals if the work of one file is spread over threads: the zpaq-std plugin codes a file's JPEG 2000 codestreams one after the other. -
Not FreePascal, but linked with it: no 64-bit division in the C on
32-bit x86.
clang -m32turns it into a call to libgcc's__divdi3, which FPC does not link;nm -uon the objects catches it (0.9.5).