The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,261 other subscribers

Be careful when using the System.Threading unit in versions earlier than Delphi 11 (it ditches Windows XP compatibility, for a reason)

Posted by jpluimers on 2022/06/07

A while ago, I got reminded of a few occurrences of Delphi apps hanging after a little less than 2 months of being active.

It does not happen that often, as usually Patch Tuesday requires a reboot, but sometimes it doesn’t, or ops forgot to patch the affected machine.

The common thing for these hanging apps was that they all used the System.Threading unit (introduced in Delphi XE7). That unit relied on [Wayback] TThread.GetTickcount (added in Delphi XE3 in the System.Classes unit) which uses a 32-bit unsigned [Archive.is] cardinal value as it relied on the Windows API [Wayback] GetTickCount function which in turn has this remark:

The elapsed time is stored as a DWORD value. Therefore, the time will wrap around to zero if the system is run continuously for 49.7 days. To avoid this problem, use the GetTickCount64 function. Otherwise, check for an overflow condition when comparing times.

The reminder was in [Wayback] Delphi 11 Windows XP compatibility tweak – RTL and Delphi Object Pascal – Delphi-PRAXiS [en] (Thanks [Wayback] Alexander Elagin), as Delphi 11 finally switched away from GetTickCount to GetTickCount64:

using GetTickCount64 in fact is a must for their code in the Threading unit which internally uses the milliseconds counter. As you know, the counter overflows every 49 or so days and some threads in your server application just hang waiting until the condition TThread.GetTickCount > FLastSuspendTick.Value + TThreadPool.SuspendInterval is fulfilled…

The multimedia Windows API function [Wayback] timeGetTime, though giving higher resolution, was no use: it takes longer to execute and is still limited to a DWORD value for Ticks:

Note that the value returned by the timeGetTime function is a DWORD value. The return value wraps around to 0 every 2^32 milliseconds, which is about 49.71 days. This can cause problems in code that directly uses the timeGetTime return value in computations, particularly where the value is used to control code execution. You should always use the difference between two timeGetTime return values in computations.

Delphi 11 switched to [Wayback] GetTickCount64, which is unavailable in Windows XP nor Windows Server 2003, and does not overflow every 49.7 days.

So basically, be sure to restart any Delphi app relying on the System.Threading unit no later than 49 days of use, or recompile with Delphi 11 and up. Or better: wait for the first Delphi 11 update (it should have appeared by now), as that will likely flesh out quite a few issues not grabbed during the field test cycle.

Note that other environments can also suffer from similar overflows:

–jeroen

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.