Hello,

In winpthreads, since we integrated test suite with Automake, we have a few 
tests marked as XFAIL:

- Some mutex tests
- Two spinlock tests (spin1.c and spin3.c)

The first two attached patches address issues with mutex tests; more details 
are in commit messages.

Current spinlock implementation in winpthreads does not keep track of lock 
ownership. It has an effect that thread A can unlock lock held by thread B; 
this behavior does not conform to POSIX specification and causes spin3.c to 
fail.

The rest of attached patches contain new spinlock implementation and updates 
for pthread_spinlock/* tests.

These changes passed CI: 
https://github.com/maiddaisuki/mingw-w64/actions/runs/25665243044. FYI, first 
run failed because of spurious failure in "gcc (msvcrt, x86_64)" job, so I had 
to re-run it.

- Kirill Makurin
From c4afcff61729a7099b71a88aad708ec370e4e29e Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:15 +0900
Subject: [PATCH 01/11] winpthreads: fix return value of pthread_mutex_unlock

If `pthread_mutex_t` object passed to `pthread_mutex_unlock` is a valid but
unlocked mutex of type `PTHREAD_MUTEX_ERRORCHECK` or `PTHREAD_MUTEX_RECURSIVE`,
`pthread_mutex_unlock` would fail with `EINVAL`.

This behavior caused tests mutex{6e,6es,7e}.c to fail since they expected
`pthread_mutex_unlock` to fail with `EPERM` in such case.

POSIX[1] says the following about `pthread_mutex_unlock`:

  The pthread_mutex_unlock() function shall fail if:

  [EPERM]
    The mutex type is PTHREAD_MUTEX_ERRORCHECK or PTHREAD_MUTEX_RECURSIVE, or
    the mutex is a robust mutex, and the current thread does not own the mutex.

It mentions `EINVAL` in RATIONALE:

  If an implementation detects that the value specified by the mutex argument
  does not refer to an initialized mutex object, it is recommended that the
  function should fail and report an [EINVAL] error.

Error condition for `EPERM` fits situation when `pthread_mutex_t` object is a
valid but unlocked mutex; it has no owner which means that the calling thread
does not own it and as such the calling thread cannot unlock it.

[1] 
https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_lock.html

Signed-off-by: Kirill Makurin <[email protected]>
---
 mingw-w64-libraries/winpthreads/src/mutex.c       | 2 +-
 mingw-w64-libraries/winpthreads/tests/Makefile.am | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/src/mutex.c 
b/mingw-w64-libraries/winpthreads/src/mutex.c
index 4022bdbbf..ae4785866 100644
--- a/mingw-w64-libraries/winpthreads/src/mutex.c
+++ b/mingw-w64-libraries/winpthreads/src/mutex.c
@@ -231,7 +231,7 @@ int pthread_mutex_unlock(pthread_mutex_t *m)
 
   if (unlikely(mi->type != Normal)) {
     if (mi->state == Unlocked)
-      return EINVAL;
+      return EPERM;
     if (mi->owner != GetCurrentThreadId())
       return EPERM;
     if (mi->rec_lock > 0) {
diff --git a/mingw-w64-libraries/winpthreads/tests/Makefile.am 
b/mingw-w64-libraries/winpthreads/tests/Makefile.am
index dc30446cb..8cbbf3816 100644
--- a/mingw-w64-libraries/winpthreads/tests/Makefile.am
+++ b/mingw-w64-libraries/winpthreads/tests/Makefile.am
@@ -175,10 +175,7 @@ TESTS = $(check_PROGRAMS)
 # TODO: investigate why these tests fail
 XFAIL_TESTS =              \
        pthread_mutex/mutex4   \
-       pthread_mutex/mutex6e  \
-       pthread_mutex/mutex6es \
        pthread_mutex/mutex7   \
-       pthread_mutex/mutex7e  \
        pthread_mutex/mutex7n  \
        pthread_spinlock/spin1 \
        pthread_spinlock/spin3
-- 
2.51.0.windows.1

From ff55b7efc48d18532779a69a728233ff163a8493 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:16 +0900
Subject: [PATCH 02/11] winpthreads: tests: fix pthread_mutex/mutex{4,7,7n}.c

POSIX[1] states that calling `pthread_mutex_unlock` on mutex of type
`PTHREAD_MUTEX_NORMAL` that is not owned by the calling thread is
undefined behavior.

An unlocked mutex fits into this category; it has no owner which means that
the calling thread does not own it.

With current, event-based implementation, `pthread_mutex_unlock` does not check
ownership for mutexes of type `PTHREAD_MUTEX_NORMAL`; this has effect that
`pthread_mutex_unlock` always succeeds as long as passed `pthread_mutex_t`
object is a valid mutex of type `PTHREAD_MUTEX_NORMAL`.

Tests mutex{4,7,7n}.c call `pthread_mutex_unlock` on an unlocked mutex of type
`PTHREAD_MUTEX_NORMAL`; they falied as they expected the old behavior, when
`pthread_mutex_unlock` would fail with `EPERM` in such case.

Update tests mutex{4,7,7n}.c to keep up with the current behavior and document
that we test winpthreads-specific behavior for a case when POSIX explicitly
specifies undefined behavior.

[1] 
https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_lock.html

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../winpthreads/tests/Makefile.am              |  3 ---
 .../winpthreads/tests/pthread_mutex/mutex4.c   | 18 ++++++++++++++++--
 .../winpthreads/tests/pthread_mutex/mutex7.c   |  9 ++++++++-
 .../winpthreads/tests/pthread_mutex/mutex7n.c  |  9 ++++++++-
 4 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/tests/Makefile.am 
b/mingw-w64-libraries/winpthreads/tests/Makefile.am
index 8cbbf3816..711b7ff8e 100644
--- a/mingw-w64-libraries/winpthreads/tests/Makefile.am
+++ b/mingw-w64-libraries/winpthreads/tests/Makefile.am
@@ -174,8 +174,5 @@ TESTS = $(check_PROGRAMS)
 
 # TODO: investigate why these tests fail
 XFAIL_TESTS =              \
-       pthread_mutex/mutex4   \
-       pthread_mutex/mutex7   \
-       pthread_mutex/mutex7n  \
        pthread_spinlock/spin1 \
        pthread_spinlock/spin3
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex4.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex4.c
index 0a5dca36a..38342b56d 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex4.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex4.c
@@ -76,7 +76,14 @@ main()
    */
   assert(pthread_create(&t, NULL, unlocker, (void *) 0) == 0);
   assert(pthread_join(t, NULL) == 0);
-  assert(pthread_mutex_unlock(&mutex1) == EPERM);
+  /**
+   * POSIX states that calling `pthread_mutex_unlock` on NORMAL mutex that is
+   * not owned by the calling thread is undefined behavior.
+   *
+   * Out implementation for NORMAL mutexes does not check ownership at all,
+   * so call to `pthread_mutex_unlock` on a valid NORMAL mutex always succeeds.
+   */
+  assert(pthread_mutex_unlock(&mutex1) == 0);
   assert(wasHere == 2);
 
   wasHere = 0;
@@ -89,7 +96,14 @@ main()
    */
   assert(pthread_create(&t, NULL, unlocker, (void *) 0) == 0);
   assert(pthread_join(t, NULL) == 0);
-  assert(pthread_mutex_unlock(&mutex1) == EPERM);
+  /**
+   * POSIX states that calling `pthread_mutex_unlock` on NORMAL mutex that is
+   * not owned by the calling thread is undefined behavior.
+   *
+   * Out implementation for NORMAL mutexes does not check ownership at all,
+   * so call to `pthread_mutex_unlock` on a valid NORMAL mutex always succeeds.
+   */
+  assert(pthread_mutex_unlock(&mutex1) == 0);
   assert(wasHere == 2);
 
   wasHere = 0;
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex7.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex7.c
index 8772b9712..f64f06ce8 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex7.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex7.c
@@ -57,7 +57,14 @@ void * locker(void * arg)
   assert(pthread_mutex_trylock(&mutex) == EBUSY);
   lockCount++;
   assert(pthread_mutex_unlock(&mutex) == 0);
-  assert(pthread_mutex_unlock(&mutex) == EPERM);
+  /**
+   * POSIX states that calling `pthread_mutex_unlock` on NORMAL mutex that is
+   * not owned by the calling thread is undefined behavior.
+   *
+   * Out implementation for NORMAL mutexes does not check ownership at all,
+   * so call to `pthread_mutex_unlock` on a valid NORMAL mutex always succeeds.
+   */
+  assert(pthread_mutex_unlock(&mutex) == 0);
 
   return 0;
 }
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex7n.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex7n.c
index 174355fca..b703f1d2d 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex7n.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/mutex7n.c
@@ -61,7 +61,14 @@ void * locker(void * arg)
   assert(pthread_mutex_trylock(&mutex) == EBUSY);
   lockCount++;
   assert(pthread_mutex_unlock(&mutex) == 0);
-  assert(pthread_mutex_unlock(&mutex) == EPERM);
+  /**
+   * POSIX states that calling `pthread_mutex_unlock` on NORMAL mutex that is
+   * not owned by the calling thread is undefined behavior.
+   *
+   * Out implementation for NORMAL mutexes does not check ownership at all,
+   * so call to `pthread_mutex_unlock` on a valid NORMAL mutex always succeeds.
+   */
+  assert(pthread_mutex_unlock(&mutex) == 0);
 
   return (void *) 555;
 }
-- 
2.51.0.windows.1
From e891f3244e63f7bf0ebf8295aa098259e6c6146a Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:16 +0900
Subject: [PATCH 03/11] winpthreads: reformat spinlock.c

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../winpthreads/src/spinlock.c                | 20 +++++++------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/src/spinlock.c 
b/mingw-w64-libraries/winpthreads/src/spinlock.c
index 452c36528..0fd8d256d 100644
--- a/mingw-w64-libraries/winpthreads/src/spinlock.c
+++ b/mingw-w64-libraries/winpthreads/src/spinlock.c
@@ -39,40 +39,34 @@
    without breaking binary compatibility.) */
 typedef intptr_t spinlock_word_t;
 
-int
-pthread_spin_init (pthread_spinlock_t *lock, int pshared)
+int pthread_spin_init (pthread_spinlock_t *lock, int pshared)
 {
   spinlock_word_t *lk = (spinlock_word_t *)lock;
   *lk = -1;
   return 0;
 }
 
-
-int
-pthread_spin_destroy (pthread_spinlock_t *lock)
+int pthread_spin_destroy (pthread_spinlock_t *lock)
 {
   return 0;
 }
 
-int
-pthread_spin_lock (pthread_spinlock_t *lock)
+int pthread_spin_lock (pthread_spinlock_t *lock)
 {
   spinlock_word_t *lk = (spinlock_word_t *)lock;
-  while (unlikely(InterlockedExchangePointer((PVOID*)lk, 0) == 0))
+  while (unlikely(InterlockedExchangePointer((PVOID*)lk, 0) == 0)) {
     YieldProcessor();
+  }
   return 0;
 }
 
-int
-pthread_spin_trylock (pthread_spinlock_t *lock)
+int pthread_spin_trylock (pthread_spinlock_t *lock)
 {
   spinlock_word_t *lk = (spinlock_word_t *)lock;
   return InterlockedExchangePointer((PVOID*)lk, 0) == 0 ? EBUSY : 0;
 }
 
-
-int
-pthread_spin_unlock (pthread_spinlock_t *lock)
+int pthread_spin_unlock (pthread_spinlock_t *lock)
 {
   spinlock_word_t *lk = (spinlock_word_t *)lock;
 #if defined(__GNUC__) || defined(__clang__)
-- 
2.51.0.windows.1

From 73dc36ae5f3da1d9ac31fb318497f6b7d1039dec Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:16 +0900
Subject: [PATCH 04/11] winpthreads: new spinlock implementation

Previous spinlock implementation did not keep track of lock ownership. It had
effect that calling `pthread_spin_unlock` from thread A would always unlock
lock held by thread B.

The old implementation caused tests spin1.c and spin3.c to fail; they expected
that implementation properly keeps track of lock ownership, which was not true.

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../winpthreads/src/spinlock.c                | 305 ++++++++++++++++--
 .../winpthreads/tests/Makefile.am             |   5 -
 2 files changed, 285 insertions(+), 25 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/src/spinlock.c 
b/mingw-w64-libraries/winpthreads/src/spinlock.c
index 0fd8d256d..8e52db714 100644
--- a/mingw-w64-libraries/winpthreads/src/spinlock.c
+++ b/mingw-w64-libraries/winpthreads/src/spinlock.c
@@ -1,6 +1,5 @@
 /*
-   Copyright (c) 2013 mingw-w64 project
-   Copyright (c) 2015 Intel Corporation
+   Copyright (c) 2026 mingw-w64 project
 
    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
@@ -25,6 +24,8 @@
 #include "config.h"
 #endif
 
+#include <stdlib.h>
+
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
@@ -33,46 +34,310 @@
 /* internal header files */
 #include "misc.h"
 
-/* We use the pthread_spinlock_t itself as a lock:
-   -1 is free, 0 is locked.
-   (This is dictated by PTHREAD_SPINLOCK_INITIALIZER, which we can't change
-   without breaking binary compatibility.) */
-typedef intptr_t spinlock_word_t;
+/**
+ * Reference:
+ *
+ * pthread_spin_init(), pthread_spin_destroy():
+ *  
<https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_spin_destroy.html>
+ *
+ * pthread_spin_lock(), pthread_spin_trylock():
+ *  
<https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_spin_lock.html>
+ *
+ * pthread_spin_unlock():
+ *  
<https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_spin_unlock.html>
+ */
+
+/**
+ * To yield or not to yield?
+ *
+ * Our implementation of `pthread_spin_lock` does not yield calling thread's
+ * CPU time if it was unable to immediately obtain the lock.
+ *
+ * One reason for this is to reduce amount of the following situations:
+ *
+ * Threads A, B and C use spin lock S. Thread A acquires S, then thread B
+ * attempts to acquire S; since A owns S, B yields its CPU time.
+ *
+ * There is a chance that A will release S and C will acquire S while B is
+ * suspended. If B is resumed while C holds S, B will yield its CPU time again.
+ * While B is suspended again, C may release S and A may acquire S again.
+ *
+ * The result is that B missed two opportunities to acquire S.
+ */
+
+/**
+ * Internal structure pointed to by `pthread_spinlock_t` objects.
+ */
+typedef struct {
+  /**
+   * This value is used to indicate that spin lock has no owner.
+   */
+#define THREAD_ID_NO_OWNER ((DWORD)-1)
+  DWORD ThreadId;
+  /**
+   * Spin lock is unlocked; `ThreadId` must be `THREAD_ID_NO_OWNER`.
+   */
+#define LOCK_UNLOCKED ((LONG)0)
+  /**
+   * Spin lock is locked; owned by `ThreadId`.
+   */
+#define LOCK_LOCKED   ((LONG)1)
+  LONG Lock;
+} WinpthreadsSpinlock;
+
+#define WINPTHREADS_SPINLOCK_INITIALIZER ((WinpthreadsSpinlock 
*)PTHREAD_SPINLOCK_INITIALIZER)
+
+/**
+ * Obtain pointer to `WinpthreadsSpinlock` structure pointed to by `lock`.
+ *
+ * If `lock` points to statically initialzied `pthread_spinlock_t` object,
+ * allocate `WinpthreadsSpinlock` structure and store its address in `*lock`.
+ *
+ * On success, stores pointer to `WinpthreadsSpinlock` structure in 
`*spinlock`.
+ *
+ * Returns zero on success and an error-code on failure.
+ */
+static WINPTHREADS_INLINE int WinpthreadsSpinlockGet (pthread_spinlock_t 
*lock, WinpthreadsSpinlock **spinlock) {
+  WinpthreadsSpinlock **pSpinlock = (WinpthreadsSpinlock **) lock;
+
+  /**
+   * POSIX:
+   *
+   * If an implementation detects that the value specified by the lock argument
+   * to pthread_spin_*() does not refer to an initialized spin lock object,
+   * it is recommended that the function should fail and report an [EINVAL] 
error.
+   */
+  if (unlikely (pSpinlock == NULL)) {
+    return EINVAL;
+  }
+
+  if (*pSpinlock == WINPTHREADS_SPINLOCK_INITIALIZER) {
+    int error_code = pthread_spin_init ((pthread_spinlock_t *) spinlock, 
PTHREAD_PROCESS_PRIVATE);
+
+    if (error_code) {
+      return error_code;
+    }
+
+    void *oldLock = InterlockedCompareExchangePointer (
+      (void **) pSpinlock, *spinlock, WINPTHREADS_SPINLOCK_INITIALIZER
+    );
+
+    /**
+     * Some other thread was faster than us.
+     */
+    if (unlikely (oldLock != WINPTHREADS_SPINLOCK_INITIALIZER)) {
+      free (*spinlock);
+      *spinlock = *pSpinlock;
+    }
+  } else {
+    *spinlock = *pSpinlock;
+  }
+
+  if (unlikely (*spinlock == NULL)) {
+    return EINVAL;
+  }
+
+  return 0;
+}
 
 int pthread_spin_init (pthread_spinlock_t *lock, int pshared)
 {
-  spinlock_word_t *lk = (spinlock_word_t *)lock;
-  *lk = -1;
+  WinpthreadsSpinlock **pSpinlock = (WinpthreadsSpinlock **) lock;
+
+  if (unlikely (pSpinlock == NULL)) {
+    return EINVAL;
+  }
+
+  if (unlikely (pshared != PTHREAD_PROCESS_PRIVATE && pshared != 
PTHREAD_PROCESS_SHARED)) {
+    return EINVAL;
+  }
+
+  if (unlikely (pshared == PTHREAD_PROCESS_SHARED)) {
+    return ENOSYS;
+  }
+
+  WinpthreadsSpinlock *wSpinlock = calloc (1, sizeof (WinpthreadsSpinlock));
+
+  /**
+   * The pthread_spin_init() function shall fail if:
+   *
+   * [ENOMEM]
+   *  Insufficient memory exists to initialize the lock.
+   */
+  if (wSpinlock == NULL) {
+    return ENOMEM;
+  }
+
+  wSpinlock->ThreadId = THREAD_ID_NO_OWNER;
+  wSpinlock->Lock     = LOCK_UNLOCKED;
+  MemoryBarrier ();
+  *pSpinlock = wSpinlock;
+
   return 0;
 }
 
 int pthread_spin_destroy (pthread_spinlock_t *lock)
 {
+  WinpthreadsSpinlock **pSpinlock = (WinpthreadsSpinlock **) lock;
+
+  /**
+   * POSIX:
+   *
+   * If an implementation detects that the value specified by the lock argument
+   * to pthread_spin_destroy() does not refer to an initialized spin lock 
object,
+   * it is recommended that the function should fail and report an [EINVAL] 
error.
+   */
+  if (unlikely (pSpinlock == NULL)) {
+    return EINVAL;
+  }
+
+  /**
+   * If `lock` points to statically initialized `pthread_spinlock_t` object,
+   * this will immediately invalidate it, minimizing the window for
+   * `pthread_spin_lock` and `pthread_spin_trylock` to attempt using it.
+   */
+  WinpthreadsSpinlock *wSpinlock = InterlockedCompareExchangePointer (
+    (void **) pSpinlock, NULL, WINPTHREADS_SPINLOCK_INITIALIZER
+  );
+
+  if (unlikely (wSpinlock == NULL)) {
+    return EINVAL;
+  }
+
+  if (unlikely (wSpinlock == WINPTHREADS_SPINLOCK_INITIALIZER)) {
+    return 0;
+  }
+
+  /**
+   * POSIX:
+   *
+   * If an implementation detects that the value specified by the lock argument
+   * to pthread_spin_destroy() or pthread_spin_init() refers to a locked spin
+   * lock object, or detects that the value specified by the lock argument to
+   * pthread_spin_init() refers to an already initialized spin lock object,
+   * it is recommended that the function should fail and report an [EBUSY] 
error.
+   */
+  if (unlikely (InterlockedExchange (&wSpinlock->Lock, LOCK_LOCKED) != 
LOCK_UNLOCKED)) {
+    return EBUSY;
+  }
+
+  /**
+   * Invalidate `pthread_spinlock_t` object pointed by `lock`.
+   */
+  InterlockedExchangePointer ((void **) pSpinlock, NULL);
+
+  /**
+   * Release the lock we are currently holding.
+   */
+  InterlockedExchange (&wSpinlock->Lock, LOCK_UNLOCKED);
+
+  /**
+   * Release all threads which still may wait for `wSpinlock`.
+   */
+  do {
+    /**
+     * We yield so other threads can acquire the lock.
+     */
+    YieldProcessor ();
+  } while (unlikely (InterlockedExchange (&wSpinlock->Lock, LOCK_UNLOCKED) == 
LOCK_LOCKED));
+
+  free (wSpinlock);
+
   return 0;
 }
 
 int pthread_spin_lock (pthread_spinlock_t *lock)
 {
-  spinlock_word_t *lk = (spinlock_word_t *)lock;
-  while (unlikely(InterlockedExchangePointer((PVOID*)lk, 0) == 0)) {
-    YieldProcessor();
+  WinpthreadsSpinlock *wSpinlock = NULL;
+
+  int error_code = WinpthreadsSpinlockGet (lock, &wSpinlock);
+
+  if (error_code) {
+    return error_code;
+  }
+
+  DWORD threadId = GetCurrentThreadId ();
+
+  /**
+   * The pthread_spin_lock() function may fail if:
+   *
+   * [EDEADLK]
+   *  A deadlock condition was detected.
+   */
+  if (unlikely (wSpinlock->ThreadId == threadId)) {
+    return EDEADLK;
+  }
+
+  while (InterlockedExchange (&wSpinlock->Lock, LOCK_LOCKED) != LOCK_UNLOCKED) 
{
+#if 0
+    YieldProcessor ();
+#endif
   }
+
+  wSpinlock->ThreadId = threadId;
+
   return 0;
 }
 
 int pthread_spin_trylock (pthread_spinlock_t *lock)
 {
-  spinlock_word_t *lk = (spinlock_word_t *)lock;
-  return InterlockedExchangePointer((PVOID*)lk, 0) == 0 ? EBUSY : 0;
+  WinpthreadsSpinlock *wSpinlock = NULL;
+
+  int error_code = WinpthreadsSpinlockGet (lock, &wSpinlock);
+
+  if (error_code) {
+    return error_code;
+  }
+
+  /**
+   * Unlike `pthread_spin_lock`, no deadlock can occur even if calling thread
+   * owns the lock.
+   *
+   * The pthread_spin_trylock() function shall fail if:
+   *
+   * [EBUSY]
+   *  A thread currently holds the lock.
+   */
+  if (InterlockedExchange (&wSpinlock->Lock, LOCK_LOCKED) != LOCK_UNLOCKED) {
+    return EBUSY;
+  }
+
+  wSpinlock->ThreadId = GetCurrentThreadId ();
+
+  return 0;
 }
 
 int pthread_spin_unlock (pthread_spinlock_t *lock)
 {
-  spinlock_word_t *lk = (spinlock_word_t *)lock;
-#if defined(__GNUC__) || defined(__clang__)
-  __atomic_store_n(lk, -1, __ATOMIC_RELEASE);
-#else
-  InterlockedExchangePointer((PVOID*)lk, (void*)-1);
-#endif
+  WinpthreadsSpinlock *wSpinlock = NULL;
+
+  int error_code = WinpthreadsSpinlockGet (lock, &wSpinlock);
+
+  if (error_code) {
+    return error_code;
+  }
+
+  if (unlikely (wSpinlock->Lock == LOCK_UNLOCKED)) {
+    return 0;
+  }
+
+  DWORD threadId = GetCurrentThreadId ();
+
+  /**
+   * POSIX:
+   *
+   * If an implementation detects that the value specified by the lock argument
+   * to pthread_spin_unlock() refers to a spin lock object for which the
+   * current thread does not hold the lock, it is recommended that the function
+   * should fail and report an [EPERM] error.
+   */
+  if (unlikely (wSpinlock->ThreadId != threadId)) {
+    return EPERM;
+  }
+
+  wSpinlock->ThreadId = THREAD_ID_NO_OWNER;
+  MemoryBarrier ();
+  wSpinlock->Lock     = LOCK_UNLOCKED;
+
   return 0;
 }
diff --git a/mingw-w64-libraries/winpthreads/tests/Makefile.am 
b/mingw-w64-libraries/winpthreads/tests/Makefile.am
index 711b7ff8e..218999d94 100644
--- a/mingw-w64-libraries/winpthreads/tests/Makefile.am
+++ b/mingw-w64-libraries/winpthreads/tests/Makefile.am
@@ -171,8 +171,3 @@ check_PROGRAMS =               \
        semaphore/semaphore5
 
 TESTS = $(check_PROGRAMS)
-
-# TODO: investigate why these tests fail
-XFAIL_TESTS =              \
-       pthread_spinlock/spin1 \
-       pthread_spinlock/spin3
-- 
2.51.0.windows.1

From 0171e466422dfb9eeb7f748c7c9f8ebedc62390e Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:16 +0900
Subject: [PATCH 05/11] winpthreads: tests: reformat pthread_spinlock/* tests

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../tests/pthread_spinlock/spin1.c            | 19 ++++-------
 .../tests/pthread_spinlock/spin2.c            | 31 +++++++----------
 .../tests/pthread_spinlock/spin3.c            | 22 ++++++------
 .../tests/pthread_spinlock/spin4.c            | 34 ++++++++-----------
 4 files changed, 45 insertions(+), 61 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c
index 00ecd11b1..a3bc0e183 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c
@@ -1,4 +1,4 @@
-/* 
+/*
  * spin1.c
  *
  *
@@ -7,25 +7,25 @@
  *      Pthreads-win32 - POSIX Threads Library for Win32
  *      Copyright(C) 1998 John E. Bossom
  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
- * 
+ *
  *      Contact Email: [email protected]
- * 
+ *
  *      The current list of contributors is contained
  *      in the file CONTRIBUTORS included with the source
  *      code distribution. The list can also be seen at the
  *      following World Wide Web location:
  *      http://sources.redhat.com/pthreads-win32/contributors.html
- * 
+ *
  *      This library is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU Lesser General Public
  *      License as published by the Free Software Foundation; either
  *      version 2 of the License, or (at your option) any later version.
- * 
+ *
  *      This library is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *      Lesser General Public License for more details.
- * 
+ *
  *      You should have received a copy of the GNU Lesser General Public
  *      License along with this library in the file COPYING.LIB;
  *      if not, write to the Free Software Foundation, Inc.,
@@ -42,17 +42,12 @@
 
 pthread_spinlock_t lock;
 
-int
-main()
+int main(void)
 {
   assert(pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) == 0);
-
   assert(pthread_spin_lock(&lock) == 0);
-
   assert(pthread_spin_unlock(&lock) == 0);
-
   assert(pthread_spin_destroy(&lock) == 0);
-
   assert(pthread_spin_lock(&lock) == EINVAL);
 
   return 0;
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
index b1d809097..939ef2c8d 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
@@ -1,4 +1,4 @@
-/* 
+/*
  * spin2.c
  *
  *
@@ -7,25 +7,25 @@
  *      Pthreads-win32 - POSIX Threads Library for Win32
  *      Copyright(C) 1998 John E. Bossom
  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
- * 
+ *
  *      Contact Email: [email protected]
- * 
+ *
  *      The current list of contributors is contained
  *      in the file CONTRIBUTORS included with the source
  *      code distribution. The list can also be seen at the
  *      following World Wide Web location:
  *      http://sources.redhat.com/pthreads-win32/contributors.html
- * 
+ *
  *      This library is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU Lesser General Public
  *      License as published by the Free Software Foundation; either
  *      version 2 of the License, or (at your option) any later version.
- * 
+ *
  *      This library is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *      Lesser General Public License for more details.
- * 
+ *
  *      You should have received a copy of the GNU Lesser General Public
  *      License along with this library in the file COPYING.LIB;
  *      if not, write to the Free Software Foundation, Inc.,
@@ -33,41 +33,34 @@
  *
  * --------------------------------------------------------------------------
  *
- * Declare a spinlock object, lock it, trylock it, 
+ * Declare a spinlock object, lock it, trylock it,
  * and then unlock it again.
  *
  */
 
 #include "test.h"
- 
+
 static int washere = 0;
 
-void * func(void * arg)
+void *func(void *arg)
 {
   assert(pthread_spin_trylock((pthread_spinlock_t *) arg) == EBUSY);
-
   washere = 1;
 
-  return 0; 
+  return 0;
 }
- 
-int
-main()
+
+int main(void)
 {
   static pthread_spinlock_t lock;
   pthread_t t;
 
   assert(pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) == 0);
-
   assert(pthread_spin_lock(&lock) == 0);
-
   assert(pthread_create(&t, NULL, func, &lock) == 0);
   assert(pthread_join(t, NULL) == 0);
-
   assert(pthread_spin_unlock(&lock) == 0);
-
   assert(pthread_spin_destroy(&lock) == 0);
-
   assert(washere == 1);
 
   return 0;
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
index 4f2ca5f0d..e0a9f28fc 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
@@ -1,4 +1,4 @@
-/* 
+/*
  * spin3.c
  *
  *
@@ -7,25 +7,25 @@
  *      Pthreads-win32 - POSIX Threads Library for Win32
  *      Copyright(C) 1998 John E. Bossom
  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
- * 
+ *
  *      Contact Email: [email protected]
- * 
+ *
  *      The current list of contributors is contained
  *      in the file CONTRIBUTORS included with the source
  *      code distribution. The list can also be seen at the
  *      following World Wide Web location:
  *      http://sources.redhat.com/pthreads-win32/contributors.html
- * 
+ *
  *      This library is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU Lesser General Public
  *      License as published by the Free Software Foundation; either
  *      version 2 of the License, or (at your option) any later version.
- * 
+ *
  *      This library is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *      Lesser General Public License for more details.
- * 
+ *
  *      You should have received a copy of the GNU Lesser General Public
  *      License along with this library in the file COPYING.LIB;
  *      if not, write to the Free Software Foundation, Inc.,
@@ -43,19 +43,19 @@
 static int wasHere = 0;
 
 static pthread_spinlock_t spin;
- 
-void * unlocker(void * arg)
+
+void *unlocker(void *arg)
 {
   int expectedResult = (int) (size_t) arg;
 
   wasHere++;
   assert(pthread_spin_unlock(&spin) == (int) expectedResult);
   wasHere++;
+
   return NULL;
 }
- 
-int
-main()
+
+int main(void)
 {
   pthread_t t;
 
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c
index f0a5abd23..0dbb111da 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c
@@ -1,4 +1,4 @@
-/* 
+/*
  * spin4.c
  *
  *
@@ -7,25 +7,25 @@
  *      Pthreads-win32 - POSIX Threads Library for Win32
  *      Copyright(C) 1998 John E. Bossom
  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
- * 
+ *
  *      Contact Email: [email protected]
- * 
+ *
  *      The current list of contributors is contained
  *      in the file CONTRIBUTORS included with the source
  *      code distribution. The list can also be seen at the
  *      following World Wide Web location:
  *      http://sources.redhat.com/pthreads-win32/contributors.html
- * 
+ *
  *      This library is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU Lesser General Public
  *      License as published by the Free Software Foundation; either
  *      version 2 of the License, or (at your option) any later version.
- * 
+ *
  *      This library is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *      Lesser General Public License for more details.
- * 
+ *
  *      You should have received a copy of the GNU Lesser General Public
  *      License along with this library in the file COPYING.LIB;
  *      if not, write to the Free Software Foundation, Inc.,
@@ -33,23 +33,24 @@
  *
  * --------------------------------------------------------------------------
  *
- * Declare a static spinlock object, lock it, spin on it, 
+ * Declare a static spinlock object, lock it, spin on it,
  * and then unlock it again.
  */
 
 #include "test.h"
 #include <sys/timeb.h>
- 
+
 pthread_spinlock_t lock = PTHREAD_SPINLOCK_INITIALIZER;
+
 struct _timeb currSysTimeStart;
 struct _timeb currSysTimeStop;
 
-#define GetDurationMilliSecs(_TStart, _TStop) 
((_TStop.time*1000+_TStop.millitm) \
-                                              - 
(_TStart.time*1000+_TStart.millitm))
+#define GetDurationMilliSecs(_TStart, _TStop) \
+  ((_TStop.time * 1000 + _TStop.millitm) - (_TStart.time * 1000 + 
_TStart.millitm))
 
 static int washere = 0;
 
-void * func(void * arg)
+void *func(void *arg)
 {
   _ftime(&currSysTimeStart);
   washere = 1;
@@ -59,9 +60,8 @@ void * func(void * arg)
 
   return (void *) (size_t) GetDurationMilliSecs(currSysTimeStart, 
currSysTimeStop);
 }
- 
-int
-main()
+
+int main(void)
 {
   intptr_t result = 0;
   pthread_t t;
@@ -71,11 +71,10 @@ main()
   if ((CPUs = pthread_num_processors_np()) == 1)
     {
       printf("Test not run - it requires multiple CPUs.\n");
-       exit(0);
+      exit(0);
     }
 
   assert(pthread_spin_lock(&lock) == 0);
-
   assert(pthread_create(&t, NULL, func, NULL) == 0);
 
   while (washere == 0)
@@ -91,12 +90,9 @@ main()
   while (GetDurationMilliSecs(currSysTimeStart, sysTime) <= 1000);
 
   assert(pthread_spin_unlock(&lock) == 0);
-
   assert(pthread_join(t, (void **) &result) == 0);
   assert(result > 1000);
-
   assert(pthread_spin_destroy(&lock) == 0);
-
   assert(washere == 1);
 
   return 0;
-- 
2.51.0.windows.1
From ea2bb4d81df5c447e98d207d257190d7e1b0d4b7 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:16 +0900
Subject: [PATCH 06/11] winpthreads: tests: update pthread_spinlock/spin1.c

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../tests/pthread_spinlock/spin1.c            | 26 +++++++++----------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c
index a3bc0e183..839e333eb 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c
@@ -1,14 +1,10 @@
 /*
- * spin1.c
- *
- *
- * --------------------------------------------------------------------------
- *
  *      Pthreads-win32 - POSIX Threads Library for Win32
  *      Copyright(C) 1998 John E. Bossom
  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *      Copyright(C) 2026 mingw-w64 project
  *
- *      Contact Email: [email protected]
+ *      Contact Email: [email protected]
  *
  *      The current list of contributors is contained
  *      in the file CONTRIBUTORS included with the source
@@ -30,22 +26,26 @@
  *      License along with this library in the file COPYING.LIB;
  *      if not, write to the Free Software Foundation, Inc.,
  *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- *
- * --------------------------------------------------------------------------
- *
- * Create a simple spinlock object, lock it, and then unlock it again.
- * This is the simplest test of the pthread mutex family that we can do.
- *
  */
 
 #include "test.h"
 
-pthread_spinlock_t lock;
+/**
+ * Test Summary:
+ *
+ * Create spinlock object and test basic assumptions about lock ownership
+ * and lifetime.
+ */
 
 int main(void)
 {
+  pthread_spinlock_t lock;
+
   assert(pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) == 0);
   assert(pthread_spin_lock(&lock) == 0);
+  assert(pthread_spin_lock(&lock) == EDEADLK);
+  assert(pthread_spin_trylock(&lock) == EBUSY);
+  assert(pthread_spin_destroy(&lock) == EBUSY);
   assert(pthread_spin_unlock(&lock) == 0);
   assert(pthread_spin_destroy(&lock) == 0);
   assert(pthread_spin_lock(&lock) == EINVAL);
-- 
2.51.0.windows.1

From 0f5968c143abc52642de4506d03d6eb625cf3b77 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:16 +0900
Subject: [PATCH 07/11] winpthreads: tests: update pthread_spinlock/spin2.c

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../tests/pthread_spinlock/spin2.c            | 40 +++++++++----------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
index 939ef2c8d..ed771b6f5 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
@@ -1,14 +1,10 @@
 /*
- * spin2.c
- *
- *
- * --------------------------------------------------------------------------
- *
  *      Pthreads-win32 - POSIX Threads Library for Win32
  *      Copyright(C) 1998 John E. Bossom
  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *      Copyright(C) 2026 mingw-w64 project
  *
- *      Contact Email: [email protected]
+ *      Contact Email: [email protected]
  *
  *      The current list of contributors is contained
  *      in the file CONTRIBUTORS included with the source
@@ -30,38 +26,40 @@
  *      License along with this library in the file COPYING.LIB;
  *      if not, write to the Free Software Foundation, Inc.,
  *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- *
- * --------------------------------------------------------------------------
- *
- * Declare a spinlock object, lock it, trylock it,
- * and then unlock it again.
- *
  */
 
 #include "test.h"
 
-static int washere = 0;
+/**
+ * Test Summary:
+ *
+ * Main thread M create spinlock object S and locks it.
+ *
+ * Thread A attempts to lock S; since S is locked by M,
+ * `pthread_spin_trylock` must fail with `EBUSY`.
+ *
+ * Thread M unlocks and destroys S.
+ */
 
-void *func(void *arg)
+static void *thread(void *arg)
 {
   assert(pthread_spin_trylock((pthread_spinlock_t *) arg) == EBUSY);
-  washere = 1;
-
-  return 0;
+  return arg;
 }
 
 int main(void)
 {
-  static pthread_spinlock_t lock;
+  pthread_spinlock_t lock;
   pthread_t t;
+  void *result;
 
   assert(pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) == 0);
   assert(pthread_spin_lock(&lock) == 0);
-  assert(pthread_create(&t, NULL, func, &lock) == 0);
-  assert(pthread_join(t, NULL) == 0);
+  assert(pthread_create(&t, NULL, thread, &lock) == 0);
+  assert(pthread_join(t, &result) == 0);
+  assert(result == &lock);
   assert(pthread_spin_unlock(&lock) == 0);
   assert(pthread_spin_destroy(&lock) == 0);
-  assert(washere == 1);
 
   return 0;
 }
-- 
2.51.0.windows.1

From 0e683a6c89b097c7b11025719abb58deac981853 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:16 +0900
Subject: [PATCH 08/11] winpthreads: tests: update pthread_spinlock/spin3.c

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../tests/pthread_spinlock/spin3.c            | 47 ++++++++-----------
 1 file changed, 20 insertions(+), 27 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
index e0a9f28fc..1b82c7571 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
@@ -1,14 +1,10 @@
 /*
- * spin3.c
- *
- *
- * --------------------------------------------------------------------------
- *
  *      Pthreads-win32 - POSIX Threads Library for Win32
  *      Copyright(C) 1998 John E. Bossom
  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *      Copyright(C) 2026 mingw-w64 project
  *
- *      Contact Email: [email protected]
+ *      Contact Email: [email protected]
  *
  *      The current list of contributors is contained
  *      in the file CONTRIBUTORS included with the source
@@ -30,43 +26,40 @@
  *      License along with this library in the file COPYING.LIB;
  *      if not, write to the Free Software Foundation, Inc.,
  *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- *
- * --------------------------------------------------------------------------
- *
- * Thread A locks spin - thread B tries to unlock.
- * This should result in a EPERM as adviced in the spec.
- *
  */
 
 #include "test.h"
 
-static int wasHere = 0;
-
-static pthread_spinlock_t spin;
+/**
+ * Test Summary:
+ *
+ * Main thread M create spinlock object S and locks it.
+ *
+ * Thread A attempts to unlock S; since S is owned by M,
+ * `pthread_spin_unlock` must fail with `EPERM`.
+ *
+ * Thread M unlocks and destroys S.
+ */
 
-void *unlocker(void *arg)
+static void *thread(void *arg)
 {
-  int expectedResult = (int) (size_t) arg;
-
-  wasHere++;
-  assert(pthread_spin_unlock(&spin) == (int) expectedResult);
-  wasHere++;
-
-  return NULL;
+  assert(pthread_spin_unlock((pthread_spinlock_t *) arg) == EPERM);
+  return arg;
 }
 
 int main(void)
 {
+  pthread_spinlock_t spin;
   pthread_t t;
+  void *result;
 
-  wasHere = 0;
   assert(pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE) == 0);
   assert(pthread_spin_lock(&spin) == 0);
-  assert(pthread_create(&t, NULL, unlocker, (void *) EPERM) == 0);
-  assert(pthread_join(t, NULL) == 0);
+  assert(pthread_create(&t, NULL, thread, &spin) == 0);
+  assert(pthread_join(t, &result) == 0);
+  assert(result == &spin);
   assert(pthread_spin_unlock(&spin) == 0);
   assert(pthread_spin_destroy(&spin) == 0);
-  assert(wasHere == 2);
 
   return 0;
 }
-- 
2.51.0.windows.1

From 644335573e40dc1b912b0d82a7039a6e288679b6 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:16 +0900
Subject: [PATCH 09/11] winpthreads: tests: update pthread_spinlock/spin4.c

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../tests/pthread_spinlock/spin4.c            | 109 +++++++++---------
 1 file changed, 54 insertions(+), 55 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c
index 0dbb111da..263133727 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c
@@ -1,14 +1,10 @@
 /*
- * spin4.c
- *
- *
- * --------------------------------------------------------------------------
- *
  *      Pthreads-win32 - POSIX Threads Library for Win32
  *      Copyright(C) 1998 John E. Bossom
  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *      Copyright(C) 2026 mingw-w64 project
  *
- *      Contact Email: [email protected]
+ *      Contact Email: [email protected]
  *
  *      The current list of contributors is contained
  *      in the file CONTRIBUTORS included with the source
@@ -30,70 +26,73 @@
  *      License along with this library in the file COPYING.LIB;
  *      if not, write to the Free Software Foundation, Inc.,
  *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- *
- * --------------------------------------------------------------------------
- *
- * Declare a static spinlock object, lock it, spin on it,
- * and then unlock it again.
  */
 
 #include "test.h"
-#include <sys/timeb.h>
 
-pthread_spinlock_t lock = PTHREAD_SPINLOCK_INITIALIZER;
+/**
+ * Test Summary:
+ *
+ * Main thread M statically initializes spin lock S and creates two threads
+ * A and B.
+ *
+ * Threads A and B both try to lock S and unlock S. Since S is statically
+ * initialized, `pthread_spin_lock` must perform actual initialization.
+ * Only one thread must complete initialization and both A and B must use
+ * the same spinlock object to perform synchronization.
+ *
+ * After both A and B terminate, M verifies that A and B used the same spinlock
+ * object, and then destroys it.
+ */
 
-struct _timeb currSysTimeStart;
-struct _timeb currSysTimeStop;
+static struct timespec ThreadAEntryTime;
+static struct timespec ThreadBEntryTime;
 
-#define GetDurationMilliSecs(_TStart, _TStop) \
-  ((_TStop.time * 1000 + _TStop.millitm) - (_TStart.time * 1000 + 
_TStart.millitm))
+static void *ThreadA(void *arg)
+{
+  pthread_spinlock_t lock;
 
-static int washere = 0;
+  assert(pthread_spin_lock((pthread_spinlock_t *) arg) == 0);
+  assert (clock_gettime (CLOCK_REALTIME, &ThreadAEntryTime) == 0);
+  lock = *(pthread_spinlock_t *) arg;
+  assert(pthread_spin_unlock((pthread_spinlock_t *) arg) == 0);
 
-void *func(void *arg)
+  return (void *) lock;
+}
+
+static void *ThreadB(void *arg)
 {
-  _ftime(&currSysTimeStart);
-  washere = 1;
-  assert(pthread_spin_lock(&lock) == 0);
-  assert(pthread_spin_unlock(&lock) == 0);
-  _ftime(&currSysTimeStop);
+  pthread_spinlock_t lock;
+
+  assert(pthread_spin_lock((pthread_spinlock_t *) arg) == 0);
+  assert (clock_gettime (CLOCK_REALTIME, &ThreadBEntryTime) == 0);
+  lock = *(pthread_spinlock_t *) arg;
+  assert(pthread_spin_unlock((pthread_spinlock_t *) arg) == 0);
 
-  return (void *) (size_t) GetDurationMilliSecs(currSysTimeStart, 
currSysTimeStop);
+  return (void *) lock;
 }
 
 int main(void)
 {
-  intptr_t result = 0;
-  pthread_t t;
-  int CPUs;
-  struct _timeb sysTime;
-
-  if ((CPUs = pthread_num_processors_np()) == 1)
-    {
-      printf("Test not run - it requires multiple CPUs.\n");
-      exit(0);
-    }
-
-  assert(pthread_spin_lock(&lock) == 0);
-  assert(pthread_create(&t, NULL, func, NULL) == 0);
-
-  while (washere == 0)
-    {
-      sched_yield();
-    }
-
-  do
-    {
-      sched_yield();
-      _ftime(&sysTime);
-    }
-  while (GetDurationMilliSecs(currSysTimeStart, sysTime) <= 1000);
-
-  assert(pthread_spin_unlock(&lock) == 0);
-  assert(pthread_join(t, (void **) &result) == 0);
-  assert(result > 1000);
+  pthread_spinlock_t lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+  pthread_t threadA;
+  pthread_spinlock_t threadALock;
+
+  pthread_t threadB;
+  pthread_spinlock_t threadBLock;
+
+  assert(pthread_create(&threadA, NULL, ThreadA, &lock) == 0);
+  assert(pthread_create(&threadB, NULL, ThreadB, &lock) == 0);
+
+  assert(pthread_join(threadA, (void **) &threadALock) == 0);
+  assert(pthread_join(threadB, (void **) &threadBLock) == 0);
+
+  wprintf(L"Thread A lock entry time: %.0f.%ld\n", (double) 
ThreadAEntryTime.tv_sec, ThreadAEntryTime.tv_nsec);
+  wprintf(L"Thread B lock entry time: %.0f.%ld\n", (double) 
ThreadBEntryTime.tv_sec, ThreadBEntryTime.tv_nsec);
+
+  assert(threadALock == threadBLock);
   assert(pthread_spin_destroy(&lock) == 0);
-  assert(washere == 1);
 
   return 0;
 }
-- 
2.51.0.windows.1

From 4fcc6af19328ed32d2166bc47db9bcfd9b676139 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:17 +0900
Subject: [PATCH 10/11] winpthreads: tests: combine pthread_spinlock/spin2.c
 and pthread_spinlock/spin3.c

Tests spin2.c and spin3.c are nearly equivalent, except they call different
`pthread_spin_*` function from new thread.

Extend spin2.c with extra checks, including those from spin3.c.

Update spin3.c to test statically initialized `pthread_spinlock_t` object.

Signed-off-by: Kirill Makurin <[email protected]>
---
 .../tests/pthread_spinlock/spin2.c            |  8 +++++
 .../tests/pthread_spinlock/spin3.c            | 32 ++++++-------------
 2 files changed, 18 insertions(+), 22 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
index ed771b6f5..77161aa90 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
@@ -38,12 +38,20 @@
  * Thread A attempts to lock S; since S is locked by M,
  * `pthread_spin_trylock` must fail with `EBUSY`.
  *
+ * Thread A attempts to unlock S; since S is owned by M,
+ * `pthread_spin_unlock` must fail with `EPERM`.
+ *
+ * Thread A attempts to destroy S; since S is locked by M,
+ * `pthread_spin_destroy` must fail with `EBUSY`.
+ *
  * Thread M unlocks and destroys S.
  */
 
 static void *thread(void *arg)
 {
   assert(pthread_spin_trylock((pthread_spinlock_t *) arg) == EBUSY);
+  assert(pthread_spin_unlock((pthread_spinlock_t *) arg) == EPERM);
+  assert(pthread_spin_destroy((pthread_spinlock_t *) arg) == EBUSY);
   return arg;
 }
 
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
index 1b82c7571..789712a7c 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
+++ b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
@@ -33,33 +33,21 @@
 /**
  * Test Summary:
  *
- * Main thread M create spinlock object S and locks it.
- *
- * Thread A attempts to unlock S; since S is owned by M,
- * `pthread_spin_unlock` must fail with `EPERM`.
- *
- * Thread M unlocks and destroys S.
+ * This test is equivalent to `spin1.c`, except it uses statically initialized
+ * `pthread_spinlock_t` object.
  */
 
-static void *thread(void *arg)
-{
-  assert(pthread_spin_unlock((pthread_spinlock_t *) arg) == EPERM);
-  return arg;
-}
-
 int main(void)
 {
-  pthread_spinlock_t spin;
-  pthread_t t;
-  void *result;
+  pthread_spinlock_t lock = PTHREAD_SPINLOCK_INITIALIZER;
 
-  assert(pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE) == 0);
-  assert(pthread_spin_lock(&spin) == 0);
-  assert(pthread_create(&t, NULL, thread, &spin) == 0);
-  assert(pthread_join(t, &result) == 0);
-  assert(result == &spin);
-  assert(pthread_spin_unlock(&spin) == 0);
-  assert(pthread_spin_destroy(&spin) == 0);
+  assert(pthread_spin_lock(&lock) == 0);
+  assert(pthread_spin_lock(&lock) == EDEADLK);
+  assert(pthread_spin_trylock(&lock) == EBUSY);
+  assert(pthread_spin_destroy(&lock) == EBUSY);
+  assert(pthread_spin_unlock(&lock) == 0);
+  assert(pthread_spin_destroy(&lock) == 0);
+  assert(pthread_spin_lock(&lock) == EINVAL);
 
   return 0;
 }
-- 
2.51.0.windows.1

From 728ee849c0fda0d1f1edce773f6f8486d024c80c Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 11 May 2026 19:40:17 +0900
Subject: [PATCH 11/11] winpthreads: tests: rename pthread_spinlock/* tests

New names now reflect the purpose of each test.

Signed-off-by: Kirill Makurin <[email protected]>
---
 mingw-w64-libraries/winpthreads/tests/Makefile.am         | 8 ++++----
 .../tests/pthread_spinlock/{spin1.c => spinlock-basic.c}  | 0
 .../pthread_spinlock/{spin3.c => spinlock-static-basic.c} | 4 ++--
 .../pthread_spinlock/{spin4.c => spinlock-static-race.c}  | 0
 .../pthread_spinlock/{spin2.c => spinlock-threaded.c}     | 0
 5 files changed, 6 insertions(+), 6 deletions(-)
 rename mingw-w64-libraries/winpthreads/tests/pthread_spinlock/{spin1.c => 
spinlock-basic.c} (100%)
 rename mingw-w64-libraries/winpthreads/tests/pthread_spinlock/{spin3.c => 
spinlock-static-basic.c} (93%)
 rename mingw-w64-libraries/winpthreads/tests/pthread_spinlock/{spin4.c => 
spinlock-static-race.c} (100%)
 rename mingw-w64-libraries/winpthreads/tests/pthread_spinlock/{spin2.c => 
spinlock-threaded.c} (100%)

diff --git a/mingw-w64-libraries/winpthreads/tests/Makefile.am 
b/mingw-w64-libraries/winpthreads/tests/Makefile.am
index 218999d94..395b598f0 100644
--- a/mingw-w64-libraries/winpthreads/tests/Makefile.am
+++ b/mingw-w64-libraries/winpthreads/tests/Makefile.am
@@ -155,10 +155,10 @@ check_PROGRAMS =               \
        pthread_rwlock/rwlock6_t2  \
        pthread_rwlock/rwlock7     \
        pthread_rwlock/rwlock8     \
-       pthread_spinlock/spin1     \
-       pthread_spinlock/spin2     \
-       pthread_spinlock/spin3     \
-       pthread_spinlock/spin4     \
+       pthread_spinlock/spinlock-basic        \
+       pthread_spinlock/spinlock-threaded     \
+       pthread_spinlock/spinlock-static-basic \
+       pthread_spinlock/spinlock-static-race  \
        pthread_tls/tsd1           \
        pthread_tls/tsd2           \
        sched/priority1            \
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-basic.c
similarity index 100%
rename from mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin1.c
rename to 
mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-basic.c
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-static-basic.c
similarity index 93%
rename from mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
rename to 
mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-static-basic.c
index 789712a7c..f754f1f24 100644
--- a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin3.c
+++ 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-static-basic.c
@@ -33,8 +33,8 @@
 /**
  * Test Summary:
  *
- * This test is equivalent to `spin1.c`, except it uses statically initialized
- * `pthread_spinlock_t` object.
+ * This test is equivalent to `spinlock-basic.c`, except it uses statically
+ * initialized `pthread_spinlock_t` object.
  */
 
 int main(void)
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-static-race.c
similarity index 100%
rename from mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin4.c
rename to 
mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-static-race.c
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c 
b/mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-threaded.c
similarity index 100%
rename from mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spin2.c
rename to 
mingw-w64-libraries/winpthreads/tests/pthread_spinlock/spinlock-threaded.c
-- 
2.51.0.windows.1

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

Reply via email to