Jump to content
mitzi

Delphi 11 Windows XP compatibility tweak

Recommended Posts

Guest

Well this still lingering in my head, i did this test and it looks working fine

var
  PerformanceCounterFreq: Int64;

function GetTickCountFromPerformanceCounter: UInt64;
begin
  QueryPerformanceCounter(Int64(Result));
  Result := Result div PerformanceCounterFreq;
end;

procedure TForm6.FormCreate(Sender: TObject);
begin
  QueryPerformanceFrequency(PerformanceCounterFreq);
  PerformanceCounterFreq := PerformanceCounterFreq div 1000;
end;

procedure TForm6.Timer1Timer(Sender: TObject);
begin
  Memo1.Lines.Add('Ticks : ' + IntToStr(GetTickCount) + '   Calc : ' + IntToStr(GetTickCountFromPerformanceCounter));
end;

and the result 

image.thumb.png.dbfe80d1d97d958f937db02ad68d1b1a.png

 

Share this post


Link to post
1 hour ago, Kas Ob. said:

Well this still lingering in my head, i did this test and it looks working fine

How about performance?

Share this post


Link to post
Guest
20 minutes ago, Fr0sT.Brutal said:

How about performance?

QueryPerformanceCounter is also not system call just like GetTickCount and GetTickCount64, and the difference should be minimum, looking at the code in the kernel it is around few tens of cycle range in worst case.

 

I don't think this should be a problem for supporting Windows XP, and it is lock free.

Now i had the emulated version of GetTickCount64 from GetTickCount is lingering in my head, and i have know how to do it.

Share this post


Link to post
Guest

As base idea i came up with the following, might be buggy though, but in general and for what i care about the most, i can and will sleep tonight better. 

var
  GTickCountHighOf64: UInt64;      // lower 32bit will be always zero
  GTickCountLastUpdated: Int64;
  GTickCountInterval: Int64;
  GTickLock: NativeUInt = 0;

const
  GTC64EMU_CHECK_INTERVAL = 1000 * 60 * 5; // in ms = 5 minutes

function GetTickCount64Emu: UInt64;

  procedure SpinLockForGTC(A: NativeUInt = 0; B: NativeUInt = 0); // parameters should be removed and replaced with registers
  asm
    // Compiles for 32bit and 64bit, we don't need PAUSE here as it will be always used for GTC64 short code
        mov     B, 1
    @LOOP:
        mov     A, [GTickLock]
        test    A, A
        jnz     @LOOP
        LOCK    cmpxchg[GTickLock], B
        jnz     @LOOP
  end;

var
  TickerDateNow, Freq: Int64;
begin
  Result := GetTickCount;
  // Will check within the specified interval after System boot and every 49.7 days, this will last for GTC64EMU_CHECK_INTERVAL
  if Result < GTC64EMU_CHECK_INTERVAL then
  begin
    SpinLockForGTC;
    try
      // update the interval in high resolution counter this will happen only once, can be in different place like initialization
      if GTickCountInterval = 0 then
      begin
        QueryPerformanceFrequency(Freq);
        GTickCountInterval := Freq * (GTC64EMU_CHECK_INTERVAL * 1000) * 2; // *2 to make sure we are far from GTC64EMU_CHECK_INTERVAL
      end;
      QueryPerformanceCounter(TickerDateNow);

      // will be true once per 49.7 days
      if TickerDateNow + GTickCountInterval > GTickCountLastUpdated then
      begin
        GTickCountLastUpdated := TickerDateNow + GTickCountInterval;     // ensure consequent (if) to be false
        Inc(GTickCountHighOf64, $100000000);
      end;
    finally
      GTickLock := 0;
    end;
  end;

  Result := Result or GTickCountHighOf64;
end;

 

Share this post


Link to post
10 hours ago, Kas Ob. said:

That works and a good solution too, but my idea is to think something locking-free.

I'm not sure this can be solved truly lock-free.  Even if you make the low-part of the counter thread-local to avoid a lock, you still have the high-part of the counter to deal with to track rollovers, and that will have to be protected from concurrent access, either with a lock, or atomic access, etc.

10 hours ago, Kas Ob. said:

But this introduce another problem, what if a thread created after 49 days from continuous running, then it will have different time by 2^32 ms, the solution is make the higher part of that ticker a global var and the lower part a local class field

I really don't know if that will work or not.  I think the entire counter needs to be global, so that new threads will start with the latest accumulated value properly.  I'm thinking something like the following, based on ideas I've seen in other implementations:

var
  TickCount64: UInt64 = 0;

function GetTickCount64Emu: UInt64;
var
  OrigValue, NewValue, TmpValue: UInt64;
  CurrTicks: UInt32;
begin
  OrigValue := TInterlocked.Read(TickCount64);
  repeat
    CurrTicks := Windows.GetTickCount;
    NewValue := (OldValue and $FFFFFFFF00000000) + CurrTicks;
    if CurrTicks < UInt32(OrigValue and $FFFFFFFF) then
      Inc(NewValue, $100000000);
    TmpValue := TInterlocked.CompareExchange(TickCount64, NewValue, OrigValue);
    if OrigValue = TmpValue then Break;
    if TmpValue > NewValue then Exit(TmpValue);
    OrigValue := TmpValue;
  until False;
  Result := NewValue;
end;
10 hours ago, Kas Ob. said:

also i expect GetTickCount64 to be loaded dynamically, so if it does not exist then use the emulated one

Yes, that would be required no matter what.

10 hours ago, Kas Ob. said:

but on other hand declaring kernel function as delayed makes no sense at all as kernel is loaded always and delaying an import for it is simply wrong,

Oh yeah, I forgot about that.  Well, then just call GetProcAddress() manually at startup (or first call) instead of using 'delayed'.  This is what Indy does.

  • Like 1

Share this post


Link to post
Guest
22 minutes ago, Remy Lebeau said:

I really don't know if that will work or not.  I think the entire counter needs to be global, so that new threads will start with the latest accumulated value properly.  I'm thinking something like the following, based on ideas I've seen in other implementations:

Nice ! and short.

About using the full 64bit it can be it can be reduced to 32bit same as i did in last post, only i made it 64bit to remove shifting with OR.

Also in your code, an interval to check and conditionally enter the loop will remove the overhead leaving it for few minutes every very long time, in other words the carry increment will only happen in well predictable time range.

27 minutes ago, Remy Lebeau said:

Oh yeah, I forgot about that.  Well, then just call GetProcAddress() manually at startup (or first call) instead of using 'delayed'.  This is what Indy does.

My point there is about the wrong doing by Delphi RTL, this most likely will increase the irritation of AV seeing delayed call for a dll that already imported statically specially for kernel API's, AV will mark these as might be injected and wasn't done by a legit compiler/linker.

Share this post


Link to post
13 hours ago, Kas Ob. said:

I don't see GetTickCount64 in Seattle, so if someone with higher version can shed some light on how it is declared, if it is delayed then it should be fixed, and replaced with dynamic address resolving, later to use it in TThread it could depend on its pointer value instead of TOSVersion.

From RSP-35459:

Quote

GetTickCount64 is marked as delayed:

535965928_ScreenShot2021-09-16at9_56_22AM.thumb.png.e31e66192b39a139aa7091ec0f8f404c.png

 

Edited by Remy Lebeau

Share this post


Link to post
6 hours ago, Kas Ob. said:

Well this still lingering in my head, i did this test and it looks working fine

The problem with using QueryPerformanceCounter()/QueryPerformanceFrequency() is that it is not consistent on all systems, especially on multi-core systems, or systems without a stable TSC (Mozilla had to write code to work around that for https://bugzilla.mozilla.org/show_bug.cgi?id=784859).  That is the main reason Indy stopped using QPC/QPF over a decade ago for the implementation of its Ticks/64() functions, now favoring GetTickCount64() instead (use of QPC/QPF can be re-enabed with a compiler define).

 

Share this post


Link to post
On 9/14/2021 at 8:52 AM, Vandrovnik said:

From my point of view, compatibility with old versions of Windows is one of important benefits of applications written in Delphi. If they broke this compatibility just because of GetTickCount64, they are throwing this benefit away needlessly.

Compatibility with versions of Windows that are no longer supported though?

Share this post


Link to post
On 9/14/2021 at 11:15 AM, Dmitry Arefiev said:

Not at all.

Why the heck not? Why waste resources supporting dead operating systems?
 

Quote


As of July 2021, 0.58% of Windows PCs run Windows XP (on all continents the share is below 1%), and 0.18% of all devices across all platforms run Windows XP.

 

 

Share this post


Link to post

 

46 minutes ago, Joseph MItzen said:

As of July 2021, 0.58% of Windows PCs run Windows XP (on all continents the share is below 1%), and 0.18% of all devices across all platforms run Windows XP.

 

I think that number is probably underestimated as many of the XP machines aren't connecting to the internet.  Just busy running 24x7 on a factory floor somewhere, in a kiosk, or cash register.  (Not to mention all the Windows XP still in use by government and military)

 

 

  • Like 4

Share this post


Link to post
Guest
1 hour ago, Darian Miller said:

I think that number is probably underestimated as many of the XP machines aren't connecting to the internet.  Just busy running 24x7 on a factory floor somewhere, in a kiosk, or cash register.  (Not to mention all the Windows XP still in use by government and military)

May be not underestimated but more like underweighted, https://theconversation.com/airports-atms-hospitals-microsoft-windows-xp-leak-would-be-less-of-an-issue-if-so-many-didnt-use-it-147018

I know for sure that huge numbers of ATM machines in the world are still running Windows XP.

 

Found this,

Though the following article is from 2014, but still relevant, i my self worked on a project two years ago to update a software running on both XP and 98, the client has huge amount of PC and their spare parts and they are working fine, but the limitation of their technology prevent him from using newer software, and the astonishing cost to update the hardware (working fine and doing-its-job-perfectly hardware) doesn't justify cost, for the owner is way cheaper to update his software, even if it called legacy.

https://www.computerworld.com/article/2700811/95-percent-of-atms-run-windows-xp--here-s-everything-you-need-to-know-about-the-security-threat.html

 

My bank now in 2021, and judging by how the forms, icons, buttons and controls looks, still running software that is compiled either Delphi 5 or Delphi 6.

Share this post


Link to post
4 hours ago, Darian Miller said:

I think that number is probably underestimated as many of the XP machines aren't connecting to the internet.  Just busy running 24x7 on a factory floor somewhere, in a kiosk, or cash register.  (Not to mention all the Windows XP still in use by government and military)

You are probably right on that, but do the software running on those systems really need to be supported by Delphi 11? I doubt it.

Share this post


Link to post
9 hours ago, Joseph MItzen said:

Compatibility with versions of Windows that are no longer supported though?

In fact, these unsupported machines created no problem this year, while supported Windows 10 created a few of problems, one of them really serious (after one actualization, printing to Kyocera printers resulted in BSOD).

Share this post


Link to post

That is not a Windows 10 problem. That is a Kyocera printer driver problem.  

Share this post


Link to post
13 minutes ago, Lars Fosdal said:

That is not a Windows 10 problem. That is a Kyocera printer driver problem.  

No, it was Windows problem (affecting Kyocera and a few others).

https://www.windowslatest.com/2021/03/10/windows-10-kb5000802-march-update-is-crashing-pcs-with-bsod/

https://www.kyoceradocumentsolutions.com/asia/en/about-us/press/20210317_news.html

Edited by Vandrovnik

Share this post


Link to post

Ah, ok. Now I recall that incident. 
With XP no longer getting patches, I am not sure if that qualifies it as more stable?

Share this post


Link to post
15 minutes ago, Lars Fosdal said:

With XP no longer getting patches, I am not sure if that qualifies it as more stable?

It is more stable in the configurations it is being used now. If nothing in the OS has broken thus far and no updates expected which can break something (as it regularily happens with "supported" OSes) I'd say XP machines will run until they break physically. Again, those computers are mostly not exposed to internet and are therefore not vulnerable to net exploits.

Share this post


Link to post

 

3 minutes ago, Alexander Elagin said:

mostly not exposed to internet and are therefore not vulnerable to net exploits.

Frequently, that turns out to be a myth.

Share this post


Link to post
11 hours ago, Darian Miller said:

probably underestimated

Yup, I have one running but don't expect newer versions of Delphi to be practical for any updates to that code..

Share this post


Link to post
1 hour ago, Alexander Elagin said:

XP machines will run until they break physically

The VMware slogan was: Run XP forever..

Share this post


Link to post
4 hours ago, Lars Fosdal said:

 

Frequently, that turns out to be a myth.

And frequently it happens to be true.

Share this post


Link to post
9 hours ago, Anders Melander said:

You are probably right on that, but do the software running on those systems really need to be supported by Delphi 11? I doubt it.

 

No, not really except you simply have another version of Delphi to support, which is typically another VM to keep around.  It would be nice to delete the old Delphi VM and simply use the latest version of RAD Studio to support all of your projects, including the ancient ones.  I know that's a big ask but in this case, it's not a major change to allow that to happen.

 

  • Like 1

Share this post


Link to post
29 minutes ago, Darian Miller said:

I know that's a big ask but in this case, it's not a major change to allow that to happen.

Sure, except you simply have another version of Windows to support, which is typically another VM to keep around.  It would be nice to delete the old Windows VM and simply use the latest version of Windows to support all of your projects, including the ancient ones. 

:classic_wink:

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×