This is a followup to '[PATCH] crt: gdtoa: Fix race conditions during 
initialization of dtoa lock'.

From 3d94cfd3408dd11fce3bc6c2384522517f8b6f2d Mon Sep 17 00:00:00 2001
From: LIU Hao <[email protected]>
Date: Mon, 14 Sep 2026 11:19:15 +0800
Subject: [PATCH] crt: gdtoa: Eliminate a race condition on `dtoa_CS_init`

When a thread saw `dtoa_CS_init == 0`, it was possible that another thread got
scheduled and completed initialization, so `dtoa_CS_init` became 2; and then the
current thread did `last_CS_init = InterlockedExchange (&dtoa_CS_init, 1)` and
saw `last_CS_init == 2` and restored it with `InterlockedExchange 
(&dtoa_CS_init, 2)`.
The value of `dtoa_CS_init` went from 2 to 1 to 2, which was an ABA pattern.

The other thread which actually completed initialization should only call
`EnterCriticalSection(&dtoa_CritSec[n])` when it saw `dtoa_CS_init == 2`. In the
middle of this ABA sequence, the call might get missed.

The fix is to update `dtoa_CS_init` to 1 with a CAS from 0 so it will not be
updated from anything else. The update to 2 is idempotent.

Signed-off-by: LIU Hao <[email protected]>
---
 mingw-w64-crt/gdtoa/misc.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/mingw-w64-crt/gdtoa/misc.c b/mingw-w64-crt/gdtoa/misc.c
index 6716392d3..74a2ca605 100644
--- a/mingw-w64-crt/gdtoa/misc.c
+++ b/mingw-w64-crt/gdtoa/misc.c
@@ -76,16 +76,13 @@ static void dtoa_lock (unsigned int n)
                return;
        }
        else if (0 == dtoa_CS_init) {
-               long last_CS_init = InterlockedExchange (&dtoa_CS_init, 1);
-               if (0 == last_CS_init) {
+               if (0 == InterlockedCompareExchange (&dtoa_CS_init, 1, 0)) {
                        int i;
                        for (i = 0; i < NLOCKS;  i++)
                                InitializeCriticalSection (&dtoa_CritSec[i]);
                        atexit (dtoa_lock_cleanup);
                        (void)InterlockedExchange (&dtoa_CS_init, 2);
                }
-               else if (2 == last_CS_init)
-                       (void)InterlockedExchange (&dtoa_CS_init, 2);
        }
        /*  Another thread is initializing. Wait. */
        while (1 == dtoa_CS_init)
@@ -98,8 +95,6 @@ static void dtoa_lock (unsigned int n)

 static void dtoa_unlock (unsigned int n)
 {
-       while (1 == dtoa_CS_init)
-               Sleep (1);
        if (2 == dtoa_CS_init)
                LeaveCriticalSection (&dtoa_CritSec[n]);
 }
--
2.55.0

From 3d94cfd3408dd11fce3bc6c2384522517f8b6f2d Mon Sep 17 00:00:00 2001
From: LIU Hao <[email protected]>
Date: Mon, 14 Sep 2026 11:19:15 +0800
Subject: [PATCH] crt: gdtoa: Eliminate a race condition on `dtoa_CS_init`

When a thread saw `dtoa_CS_init == 0`, it was possible that another thread got
scheduled and completed initialization, so `dtoa_CS_init` became 2; and then the
current thread did `last_CS_init = InterlockedExchange (&dtoa_CS_init, 1)` and
saw `last_CS_init == 2` and restored it with `InterlockedExchange 
(&dtoa_CS_init, 2)`.
The value of `dtoa_CS_init` went from 2 to 1 to 2, which was an ABA pattern.

The other thread which actually completed initialization should only call
`EnterCriticalSection(&dtoa_CritSec[n])` when it saw `dtoa_CS_init == 2`. In the
middle of this ABA sequence, the call might get missed.

The fix is to update `dtoa_CS_init` to 1 with a CAS from 0 so it will not be
updated from anything else. The update to 2 is idempotent.

Signed-off-by: LIU Hao <[email protected]>
---
 mingw-w64-crt/gdtoa/misc.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/mingw-w64-crt/gdtoa/misc.c b/mingw-w64-crt/gdtoa/misc.c
index 6716392d3..74a2ca605 100644
--- a/mingw-w64-crt/gdtoa/misc.c
+++ b/mingw-w64-crt/gdtoa/misc.c
@@ -76,16 +76,13 @@ static void dtoa_lock (unsigned int n)
                return;
        }
        else if (0 == dtoa_CS_init) {
-               long last_CS_init = InterlockedExchange (&dtoa_CS_init, 1);
-               if (0 == last_CS_init) {
+               if (0 == InterlockedCompareExchange (&dtoa_CS_init, 1, 0)) {
                        int i;
                        for (i = 0; i < NLOCKS;  i++)
                                InitializeCriticalSection (&dtoa_CritSec[i]);
                        atexit (dtoa_lock_cleanup);
                        (void)InterlockedExchange (&dtoa_CS_init, 2);
                }
-               else if (2 == last_CS_init)
-                       (void)InterlockedExchange (&dtoa_CS_init, 2);
        }
        /*  Another thread is initializing. Wait. */
        while (1 == dtoa_CS_init)
@@ -98,8 +95,6 @@ static void dtoa_lock (unsigned int n)
 
 static void dtoa_unlock (unsigned int n)
 {
-       while (1 == dtoa_CS_init)
-               Sleep (1);
        if (2 == dtoa_CS_init)
                LeaveCriticalSection (&dtoa_CritSec[n]);
 }
-- 
2.55.0

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to