Re: [PATCH v1] Fix races in Windows pthread emulation

From: Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
To: Nazir Bilal Yavuz <byavuz81(at)gmail(dot)com>, Harrison Booth <harrisontbooth(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: [PATCH v1] Fix races in Windows pthread emulation
Date: 2026-10-05 15:48:51
Message-ID: e39c8c80-5554-4bc1-a232-aaa552e556d9@iki.fi
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 20/08/2026 15:09, Nazir Bilal Yavuz wrote:
> On Fri, 24 Jul 2026 at 10:07, Harrison Booth <harrisontbooth(at)gmail(dot)com> wrote:
>>
>> On native Windows ARM64, the ECPG thread/alloc test could hang until
>> Meson's 1000-second timeout. The cause was a race in the Windows pthread
>> mutex emulation.
>
> I ran into this exact issue today [1] and found your thread.

Is there some memory ordering reason why this only happens on ARM64?
AFAICS, it could happen on x86 too.

>> The mutex initialization path used InterlockedExchange to set initstate to
>> 2. A waiting thread could therefore change the fully initialized state from
>> 1 back to 2. pthread_mutex_unlock would then see a state other than 1,
>> return EINVAL, and leave the critical section locked.
>
> I tried to understand the problem and the related Windows thread
> functions, please correct my understanding:
>
> 1- In pthread_mutex_lock(), calling
> InterlockedExchange(&mp->initstate, 2) writes unconditionally,
> clobbering an initialized state 1 back to 2.
> 2- The thread enters the critical section while mp->initstate = 2.
> 3- Later, when pthread_mutex_unlock() is called, if (mp->initstate !=
> 1) evaluates to true, causing the function to return EINVAL without
> releasing the underlying critical section, leading to a deadlock.
>
> And your patch fixes this problem with
> InterlockedCompareExchange(&mp->initstate, 2, 0), because now you
> check the initial value so that you can't overwrite 1 with 2.

That works, but an InterlockedCompareExchange is more expensive than a
plain unlocked read. I presume the reason it didn't use
InterlockedCompareExchange in the first place. I think you could salvage
it by doing an unlocked check first (with a memory barrier?), and using
InterLockedCompareExchange only if the initialization is needed.

I don't think we need to care too much about performance of ecpg on
Windows -- I doubt anyone's doing anything that performance sensitive
with that combo -- but nevertheless.

> -typedef bool pthread_once_t;
> +typedef LONG pthread_once_t;

Is that safe to backpatch?

- Heikki

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Masahiko Sawada 2026-10-05 16:10:56 Re: parallel autovacuum: Propagate track_cost_delay_timing to parallel workers
Previous Message shihao zhong 2026-10-05 15:46:54 Re: REPACK (CONCURRENTLY) might keep dropped-column data