Re: pg_threads.h take II

From: Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
To: Thomas Munro <thomas(dot)munro(at)gmail(dot)com>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>, Jelte Fennema <postgres(at)jeltef(dot)nl>, Bryan Green <dbryan(dot)green(at)gmail(dot)com>
Subject: Re: pg_threads.h take II
Date: 2026-07-06 20:48:16
Message-ID: 39ecf6c0-e4a1-43ad-b6c7-1b8bc7d10958@iki.fi
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 06/07/2026 15:09, Thomas Munro wrote:
> Hi,
>
> Here's another go at port/pg_threads.h, a C11 <threads.h>-like
> interface, with some patches to use it (some just sketch-quality).
> Previous work:
>
> https://www.postgresql.org/message-id/flat/CA%2BhUKGLtmexrpMtxBRLCVePqV_dtWG-ZsEbyPrYc%2BNBB2TkNsw%40mail.gmail.com
>
> We still can't require C11 <threads.h> yet, though the interface has a
> clear future upgrade/remap path to do that eventually. Since I last
> wrote about this topic, we gained the ability to count on C11
> thread_local being available (it's a language/compiler feature and
> doesn't need a <threads.h> header or library), and partly because of
> that, but also because I don't think anyone really likes it, I ripped
> out that tss_t stuff. I came up with a narrower API to get
> thread-exit cleanup callbacks if you need them for your thread_local
> resources, same pthread_key_t/FlsAlloc stuff underneath it but without
> having to get lost in the weeds of <threads.h> conformance.

Nice!

I've now reviewed patches 0001-0007. I think they're ready to be
committed, with some small comments below:

> v1-0002-port-Provide-minimal-pg_threads.h-API.patch
>
> The main thing I'm wondering about is how to make the error reporting
> a bit nicer. Neither pthreads nor C11 threads set errno, but it's
> nice if you can use %m. Hmm.

Perhaps provide a function like:

void
set_errno_from_pg_thrd_error(int error)
{
switch((pg_thrd_error_t) error)
{
case pg_thrd_nomem:
errno = ENOMEM;
break;
case pg_thrd_busy:
errno = EBUSY;
break;
...
}
}

> +/* Convert native error to pg_thrd_error_t. */
> +static inline int
> +pg_thrd_maperror(int error)
> +{
> +#ifdef WIN32
> + return error ? pg_thrd_success : pg_thrd_error;
> +#else
> + return error == 0 ? pg_thrd_success : pg_thrd_error;
> +#endif
> +}

The WIN32 version of this is confusing. There is only one caller of this
with WIN32:

> +#elif defined(WIN32)
> + return pg_thrd_maperror(InitializeSynchronizationBarrier(barrier, count, 0));
> +#else

So that's not wrong. But I'd suggest only defining pg_thrd_maperror()
with pthreads, and handling that one WIN32 caller directly without the
helper function. Maybe rename pg_thrd_maperror() to something like
map_pthread_retval_to_pg_thrd_error() or something.

Do the pthread functions return an errno on error? Currently, all errors
get squashed to pg_thrd_error, which is a little unfortunate.

> +/* Like C11 mtx_type_t. */
> +typedef enum pg_mtx_type_t
> +{
> + pg_mtx_plain = 0
> +} pg_mtx_type_t;
> +
> +/* Like C11 mtx_init(). */
> +static inline int
> +pg_mtx_init(pg_mtx_t *mutex, int type)
> +{
> +#ifdef WIN32
> + return pg_rwlock_init(mutex);
> +#else
> + return pg_thrd_maperror(pthread_mutex_init(mutex, NULL));
> +#endif
> +}

Since we only support plain mutexes, how about "Assert(type ==
pg_mtx_plan)" here?

> +/*-------------------------------------------------------------------------
> + *
> + * Barriers. Not in C11.
> + *
> + *-------------------------------------------------------------------------
> + */

I wonder if we really need barriers. They're not that useful IMHO. I'd
tend to just open-code this directly with a mutex and the condition
variable in most cases. (Not a strong objection, there's little harm in
having it either)

> +/*
> + * Wait for all expected threads to arrive at the barrier, and elect one
> + * arbitrary thread to perform a phase of computation serially. Sets
> + * *elected_thread to true in the elected thread, and false in all others.
> + */
> +static inline int
> +pg_barrier_wait_and_elect(pg_barrier_t *barrier, bool *elected_thread)
> +{

No callers use the *elected_thread return value. What was the idea here?

> +#ifdef WIN32
> +
> + /*
> + * Retrieve handle passed here by pg_thrd_create() before allowing this
> + * thread to run. (pg_thrd_current() can't use CurrentThread(), because
> + * that returns a pseudo-handle with the same value in all threads.)
> + */
> + Assert(start_info->self);
> + my_thrd_handle = start_info->self;
> +#endif

Does that refer to the GetCurrentThreadId() function? We use that in a
few places currently.

How can it return the same value in all threads, isn't that completely
useless? And does that mean all our current uses of it are broken?

- Heikki

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message dbryan.green 2026-07-06 21:01:10 Re: pg_threads.h take II
Previous Message Noah Misch 2026-07-06 20:44:42 Re: PROPERTY GRAPH pg_dump ACL minimization