1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 */
15
16 /*
17 * Verify we can set and obtain various mutex attributes in the structure. This
18 * is also a regression test for illumos#17334 where we did not get the type
19 * correctly. This does not validate that a mutex can be successfully created
20 * with these attributes.
21 */
22
23 #include <stdlib.h>
24 #include <err.h>
25 #include <pthread.h>
26 #include <sys/sysmacros.h>
27 #include <stdbool.h>
28 #include <errno.h>
29 #include <string.h>
30
31 typedef int (*attr_set_f)(pthread_mutexattr_t *, int);
32 typedef int (*attr_get_f)(const pthread_mutexattr_t *, int *);
33
34 static const int check_types[] = { PTHREAD_MUTEX_NORMAL,
35 PTHREAD_MUTEX_ERRORCHECK, PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_DEFAULT };
36 static const int check_shared[] = { PTHREAD_PROCESS_SHARED,
37 PTHREAD_PROCESS_PRIVATE };
38 static const int check_prioceil[] = { 0, 1, 2 };
39 static const int check_protocol[] = { PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT,
40 PTHREAD_PRIO_PROTECT };
41 static const int check_robust[] = { PTHREAD_MUTEX_STALLED, PTHREAD_MUTEX_ROBUST,
42 PTHREAD_MUTEX_STALL_NP, PTHREAD_MUTEX_ROBUST_NP };
43
44 static bool
check_field(const char * desc,pthread_mutexattr_t * attr,attr_get_f get_f,attr_set_f set_f,int def,const int * vals,size_t nvals,int err_code)45 check_field(const char *desc, pthread_mutexattr_t *attr, attr_get_f get_f,
46 attr_set_f set_f, int def, const int *vals, size_t nvals, int err_code)
47 {
48 bool ret = true;
49 int r, v;
50
51 if ((r = get_f(attr, NULL)) != EINVAL) {
52 warnx("TEST FAILED: expected getting %s attribute with invalid "
53 "attr structure to return EINVAL, found %s", desc,
54 strerrorname_np(r));
55 ret = false;
56 } else {
57 (void) printf("TEST PASSED: getting attribute %s with invalid "
58 "attributes returned EINVAL\n");
59 }
60
61 if ((r = get_f(attr, &v)) != 0) {
62 warnc(r, "TEST FAILED: failed to get default value for mutex "
63 "%s", desc);
64 ret = false;
65 } else if (v != def) {
66 warnx("TEST FAILED: mutex %s has wrong default value: expected "
67 "0x%x, found 0x%x", desc, def, v);
68 ret = false;
69 } else {
70 (void) printf("TEST PASSED: mutex %s default value is the "
71 "expected value\n", desc);
72 }
73
74 for (size_t i = 0; i < nvals; i++) {
75 if ((r = set_f(attr, vals[i])) != 0) {
76 warnc(r, "TEST FAILED: failed to set mutex %s "
77 "attribute to 0x%x", desc, vals[i]);
78 ret = false;
79 continue;
80 }
81
82 if ((r = get_f(attr, &v)) != 0) {
83 warnc(r, "TEST FAILED: failed to get value for mutex "
84 "%s", desc);
85 ret = false;
86 } else if (v != vals[i]) {
87 warnx("TEST FAILED: mutex %s has wrong value: expected "
88 "0x%x, found 0x%x", desc, vals[i], v);
89 ret = false;
90 } else {
91 (void) printf("TEST PASSED: mutex %s value matches "
92 "what we just set (0x%x)\n", desc, vals[i]);
93 }
94 }
95
96 if ((r = set_f(attr, INT32_MAX)) != err_code) {
97 warnx("TEST FAILED: expected setting mutex %s to INT32_MAX to "
98 "fail with %s, got %s (0x%x)", desc,
99 strerrorname_np(err_code), strerrorname_np(r), r);
100 ret = false;
101 } else {
102 (void) printf("TEST PASSED: Setting mutex %s to invalid value "
103 "(INT32_MAX) correctly failed with %s\n", desc,
104 strerrorname_np(err_code));
105 }
106
107 if ((r = set_f(attr, INT32_MIN)) != err_code) {
108 warnx("TEST FAILED: expected setting mutex %s to INT32_MIN to "
109 "fail with %s, got %s (0x%x)", desc,
110 strerrorname_np(err_code), strerrorname_np(r), r);
111 ret = false;
112 } else {
113 (void) printf("TEST PASSED: Setting mutex %s to invalid value "
114 "(INT32_MIN) correctly failed with %s\n", desc,
115 strerrorname_np(err_code));
116 }
117
118 return (ret);
119 }
120
121 int
main(void)122 main(void)
123 {
124 int ret = EXIT_SUCCESS, r;
125 pthread_mutexattr_t attr;
126
127 if ((r = pthread_mutexattr_init(&attr)) != 0) {
128 errc(EXIT_FAILURE, r, "TEST FAILED: failed to initialize "
129 "mutex attributes");
130 }
131
132 if (!check_field("type", &attr, pthread_mutexattr_gettype,
133 pthread_mutexattr_settype, PTHREAD_MUTEX_DEFAULT, check_types,
134 ARRAY_SIZE(check_types), EINVAL)) {
135 ret = EXIT_FAILURE;
136 }
137
138 if (!check_field("shared", &attr, pthread_mutexattr_getpshared,
139 pthread_mutexattr_setpshared, PTHREAD_PROCESS_PRIVATE, check_shared,
140 ARRAY_SIZE(check_shared), EINVAL)) {
141 ret = EXIT_FAILURE;
142 }
143
144 if (!check_field("priority ceiling", &attr,
145 pthread_mutexattr_getprioceiling, pthread_mutexattr_setprioceiling,
146 0, check_prioceil, ARRAY_SIZE(check_prioceil), EINVAL)) {
147 ret = EXIT_FAILURE;
148 }
149
150 if (!check_field("protocol", &attr, pthread_mutexattr_getprotocol,
151 pthread_mutexattr_setprotocol, PTHREAD_PRIO_NONE, check_protocol,
152 ARRAY_SIZE(check_protocol), ENOTSUP)) {
153 ret = EXIT_FAILURE;
154 }
155
156 if (!check_field("robust", &attr, pthread_mutexattr_getrobust,
157 pthread_mutexattr_setrobust, PTHREAD_MUTEX_STALLED, check_robust,
158 ARRAY_SIZE(check_robust), EINVAL)) {
159 ret = EXIT_FAILURE;
160 }
161
162 if ((r = pthread_mutexattr_destroy(&attr)) != 0) {
163 warnc(r, "TEST FAILED: failed to destroy mutex attributes");
164 ret = EXIT_FAILURE;
165 }
166
167 if (ret == EXIT_SUCCESS) {
168 (void) printf("All tests passed successfully\n");
169 }
170
171 return (ret);
172 }
173