winpthreads: mutex.c: update `pthread_mutexattr_*` functions

Interpret `pthread_mutexattr_t` as a union consisting of bit fields.

This change makes it easier to understand how `pthread_mutexattr_*` functions
operate on `pthread_mutexattr_t` objects and provide support for new attributes.

Remove macro `PTHREAD_PRIO_MULT` from pthread.h; this constant is not defined
by POSIX and is only used by `pthread_mutexattr_{set,get}prioceiling` functions.

Signed-off-by: Kirill Makurin <maiddaisuki@outlook.com>
Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-libraries/winpthreads/include/pthread.h b/mingw-w64-libraries/winpthreads/include/pthread.h
index 3ab5db7..f9eba44 100644
--- a/mingw-w64-libraries/winpthreads/include/pthread.h
+++ b/mingw-w64-libraries/winpthreads/include/pthread.h
@@ -128,7 +128,6 @@
 #define PTHREAD_PRIO_NONE 0
 #define PTHREAD_PRIO_INHERIT 8
 #define PTHREAD_PRIO_PROTECT 16
-#define PTHREAD_PRIO_MULT 32
 #define PTHREAD_PROCESS_SHARED 1
 #define PTHREAD_PROCESS_PRIVATE 0
 
diff --git a/mingw-w64-libraries/winpthreads/src/mutex.c b/mingw-w64-libraries/winpthreads/src/mutex.c
index 3b3edf2..93b854f 100644
--- a/mingw-w64-libraries/winpthreads/src/mutex.c
+++ b/mingw-w64-libraries/winpthreads/src/mutex.c
@@ -90,6 +90,11 @@
 typedef union WinpthreadsMutex WinpthreadsMutex;
 
 /**
+ * Forward declaration; see definition below.
+ */
+typedef union WinpthreadsMutexAttributes WinpthreadsMutexAttributes;
+
+/**
  * Mutex type-specific "init" routine.
  */
 typedef int (* FuncMutexInit) (WinpthreadsMutex **, const pthread_mutexattr_t *);
@@ -130,6 +135,71 @@
   FuncMutexUnlock Unlock;
 } WinpthreadsMutexVtable;
 
+/**
+ * Union stored in `pthread_mutexattr_t` objects.
+ */
+union WinpthreadsMutexAttributes {
+  pthread_mutexattr_t Value;
+  struct {
+    /**
+     * Can be one of the following values:
+     *
+     * - PTHREAD_MUTEX_NORMAL
+     * - PTHREAD_MUTEX_ERRORCHECK
+     * - PTHREAD_MUTEX_RECURSIVE
+     */
+    int Type            : 4;
+    /**
+     * Zero for `PTHREAD_PROCESS_PRIVATE` and non-zero for
+     * `PTHREAD_PROCESS_SHARED`.
+     *
+     * Currently, winpthreads only supports `PTHREAD_PROCESS_PRIVATE`.
+     */
+    int Shared          : 1;
+    int Reserved        : 3;
+    /**
+     * Can be one of the following values:
+     *
+     * - PTHREAD_PRIO_NONE
+     * - PTHREAD_PRIO_INHERIT
+     * - PTHREAD_PRIO_PROTECT
+     *
+     * Currently, winpthreads does not implement POSIX realtime extensions.
+     * As such, the only supported value is `PTHREAD_PRIO_NONE`.
+     */
+    int Protocol        : 8;
+    /**
+     * Can be any valid `THREAD_PRIORITY_*` value.
+     *
+     * Only has effect when `Protocol` is `PTHREAD_PRIO_INHERIT` or
+     * `PTHREAD_PRIO_PROTECT`, which are not currently supported.
+     */
+    int PriorityCeiling : 8;
+#define WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC 0x67
+    /**
+     * Must be `WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC`.
+     *
+     * If needed, these bits may be used for future extensions.
+     */
+    int Magic           : 8;
+  } Attributes;
+};
+
+WINPTHREADS_STATIC_ASSERT (sizeof ((WinpthreadsMutexAttributes) {}.Attributes) == sizeof (pthread_mutexattr_t), "");
+
+/**
+ * Expands into an expression of type `WinpthreadsMutexAttributes`
+ * corresponding to the default mutex attributes.
+ */
+#define WINPTHREADS_MUTEX_ATTRIBUTES_DEFAULT ((WinpthreadsMutexAttributes) { \
+    .Attributes.Type            = PTHREAD_MUTEX_NORMAL,                      \
+    .Attributes.Shared          = PTHREAD_PROCESS_PRIVATE,                   \
+    .Attributes.Reserved        = 0,                                         \
+    .Attributes.Protocol        = PTHREAD_PRIO_NONE,                         \
+    .Attributes.PriorityCeiling = THREAD_PRIORITY_NORMAL,                    \
+    .Attributes.Magic           = WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC         \
+  })
+
 #define THREAD_ID_NO_OWNER ((DWORD) -1)
 
 /**
@@ -1038,102 +1108,197 @@
 
 int pthread_mutexattr_init(pthread_mutexattr_t *a)
 {
-  *a = PTHREAD_MUTEX_NORMAL | (PTHREAD_PROCESS_PRIVATE << 3);
+  *a = WINPTHREADS_MUTEX_ATTRIBUTES_DEFAULT.Value;
   return 0;
 }
 
 int pthread_mutexattr_destroy(pthread_mutexattr_t *a)
 {
-  if (!a)
+  if (a == NULL) {
     return EINVAL;
-
-  return 0;
-}
-
-int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type)
-{
-  if (!a || !type)
-    return EINVAL;
-
-  *type = *a & 3;
-
-  return 0;
-}
-
-int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type)
-{
-  if (!a)
-    return EINVAL;
-
-  if (type != PTHREAD_MUTEX_NORMAL && type != PTHREAD_MUTEX_RECURSIVE && type != PTHREAD_MUTEX_ERRORCHECK)
-    return EINVAL;
-
-  *a &= ~3;
-  *a |= type;
-
-  return 0;
-}
-
-int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type)
-{
-  if (!a || !type)
-    return EINVAL;
-
-  *type = (*a & 4 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE);
-
-  return 0;
-}
-
-int pthread_mutexattr_setpshared(pthread_mutexattr_t *a, int type)
-{
-  int r = 0;
-
-  if (!a)
-    return EINVAL;
-
-  if (type != PTHREAD_PROCESS_SHARED && type != PTHREAD_PROCESS_PRIVATE)
-    return EINVAL;
-
-  if (type == PTHREAD_PROCESS_SHARED) {
-    type = PTHREAD_PROCESS_PRIVATE;
-    r = ENOSYS;
   }
 
-  type = (type == PTHREAD_PROCESS_SHARED ? 4 : 0);
-
-  *a &= ~4;
-  *a |= type;
-
-  return r;
-}
-
-int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type)
-{
-  *type = *a & (8 + 16);
+  memset (a, 0, sizeof (pthread_mutexattr_t));
   return 0;
 }
 
-int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type)
+int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *value)
 {
-  if ((type & (8 + 16)) != 8 + 16)
+  if (a == NULL || value == NULL) {
     return EINVAL;
+  }
 
-  *a &= ~(8 + 16);
-  *a |= type;
+  const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a;
 
+  if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
+    return EINVAL;
+  }
+
+  switch (wMutexAttr->Attributes.Type) {
+    case PTHREAD_MUTEX_NORMAL:
+    case PTHREAD_MUTEX_ERRORCHECK:
+    case PTHREAD_MUTEX_RECURSIVE:
+      *value = wMutexAttr->Attributes.Type;
+      return 0;
+  }
+
+  return EINVAL;
+}
+
+int pthread_mutexattr_settype(pthread_mutexattr_t *a, int value)
+{
+  if (a == NULL) {
+    return EINVAL;
+  }
+
+  WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a;
+
+  if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
+    return EINVAL;
+  }
+
+  switch (value) {
+    case PTHREAD_MUTEX_NORMAL:
+    case PTHREAD_MUTEX_ERRORCHECK:
+    case PTHREAD_MUTEX_RECURSIVE:
+      wMutexAttr->Attributes.Type = value;
+      return 0;
+  }
+
+  return EINVAL;
+}
+
+int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *value)
+{
+  if (a == NULL || value == NULL) {
+    return EINVAL;
+  }
+
+  const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a;
+
+  if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
+    return EINVAL;
+  }
+
+  *value = !!wMutexAttr->Attributes.Shared;
   return 0;
 }
 
-int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int *prio)
+int pthread_mutexattr_setpshared(pthread_mutexattr_t *a, int value)
 {
-  *prio = *a / PTHREAD_PRIO_MULT;
-  return 0;
+  if (a == NULL) {
+    return EINVAL;
+  }
+
+  WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a;
+
+  if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
+    return EINVAL;
+  }
+
+  switch (value) {
+    case PTHREAD_PROCESS_PRIVATE:
+    case PTHREAD_PROCESS_SHARED:
+      wMutexAttr->Attributes.Shared = value;
+      return 0;
+  }
+
+  return EINVAL;
 }
 
-int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio)
+int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *value)
 {
-  *a &= (PTHREAD_PRIO_MULT - 1);
-  *a += prio * PTHREAD_PRIO_MULT;
+  if (a == NULL || value == NULL) {
+    return EINVAL;
+  }
 
-  return 0;
+  const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a;
+
+  if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
+    return EINVAL;
+  }
+
+  switch (wMutexAttr->Attributes.Protocol) {
+    case PTHREAD_PRIO_NONE:
+    case PTHREAD_PRIO_INHERIT:
+    case PTHREAD_PRIO_PROTECT:
+      *value = wMutexAttr->Attributes.Protocol;
+      return 0;
+  }
+
+  return EINVAL;
+}
+
+int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int value)
+{
+  if (a == NULL) {
+    return EINVAL;
+  }
+
+  WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a;
+
+  if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
+    return EINVAL;
+  }
+
+  switch (value) {
+    case PTHREAD_PRIO_NONE:
+      wMutexAttr->Attributes.Protocol = value;
+      return 0;
+    /**
+     * POSIX realtime extensions are not implemented.
+     *
+     * The pthread_mutexattr_setprotocol() function shall fail if:
+     *
+     * [ENOTSUP]
+     *   The value specified by protocol is an unsupported value.
+     */
+    case PTHREAD_PRIO_INHERIT:
+    case PTHREAD_PRIO_PROTECT:
+      return ENOTSUP;
+  }
+
+  return EINVAL;
+}
+
+int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int *value)
+{
+  if (a == NULL || value == NULL) {
+    return EINVAL;
+  }
+
+  const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a;
+
+  if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
+    return EINVAL;
+  }
+
+  int priorityCeiling = wMutexAttr->Attributes.PriorityCeiling;
+
+  if (priorityCeiling >= THREAD_PRIORITY_IDLE && priorityCeiling <= THREAD_PRIORITY_TIME_CRITICAL) {
+    *value = priorityCeiling;
+    return 0;
+  }
+
+  return EINVAL;
+}
+
+int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int value)
+{
+  if (a == NULL) {
+    return EINVAL;
+  }
+
+  WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a;
+
+  if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
+    return EINVAL;
+  }
+
+  if (value >= THREAD_PRIORITY_IDLE && value <= THREAD_PRIORITY_TIME_CRITICAL) {
+    wMutexAttr->Attributes.PriorityCeiling = value;
+    return 0;
+  }
+
+  return EINVAL;
 }