xref: /netbsd-src/external/mpl/bind/dist/lib/isc/include/isc/util.h (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: util.h,v 1.17 2025/01/26 16:25:43 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #pragma once
17 
18 #include <inttypes.h>
19 
20 /*! \file isc/util.h
21  * NOTE:
22  *
23  * This file is not to be included from any <isc/???.h> (or other) library
24  * files.
25  *
26  * \brief
27  * Including this file puts several macros in your name space that are
28  * not protected (as all the other ISC functions/macros do) by prepending
29  * ISC_ or isc_ to the name.
30  */
31 
32 #include <isc/attributes.h>
33 
34 /***
35  *** Clang Compatibility Macros
36  ***/
37 
38 #if !defined(__has_feature)
39 #define __has_feature(x) 0
40 #endif /* if !defined(__has_feature) */
41 
42 /***
43  *** General Macros.
44  ***/
45 
46 /*%
47  * Legacy way how to hide unused function arguments, don't use in
48  * the new code, rather use the ISC_ATTR_UNUSED macro that expands
49  * to either C23's [[maybe_unused]] or __attribute__((__unused__)).
50  *
51  * \code
52  * int
53  * foo(ISC_ATTR_UNUSED char *bar) {
54  *         ...;
55  * }
56  * \endcode
57  */
58 #define UNUSED(x) (void)(x)
59 
60 #if __GNUC__ >= 8 && !defined(__clang__)
61 #define ISC_NONSTRING __attribute__((nonstring))
62 #else /* if __GNUC__ >= 8 && !defined(__clang__) */
63 #define ISC_NONSTRING
64 #endif /* __GNUC__ */
65 
66 #if HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR && HAVE_FUNC_ATTRIBUTE_DESTRUCTOR
67 #define ISC_CONSTRUCTOR __attribute__((constructor))
68 #define ISC_DESTRUCTOR	__attribute__((destructor))
69 #else
70 #define ISC_CONSTRUCTOR
71 #define ISC_DESTRUCTOR
72 #endif
73 
74 /*%
75  * The opposite: silent warnings about stored values which are never read.
76  */
77 #define POST(x) (void)(x)
78 
79 #define ISC_MAX(a, b) ((a) > (b) ? (a) : (b))
80 #define ISC_MIN(a, b) ((a) < (b) ? (a) : (b))
81 
82 #define ISC_CLAMP(v, x, y) ((v) < (x) ? (x) : ((v) > (y) ? (y) : (v)))
83 
84 /*%
85  * The UNCONST() macro can be used to omit warnings produced by certain
86  * compilers when operating with pointers declared with the const type qual-
87  * ifier in a context without such qualifier.  Examples include passing a
88  * pointer declared with the const qualifier to a function without such
89  * qualifier, and variable assignment from a const pointer to a non-const
90  * pointer.
91  *
92  * As the macro may hide valid errors, their usage is not recommended
93  * unless there is a well-thought reason for a cast.  A typical use case for
94  * __UNCONST() involve an API that does not follow the so-called ``const
95  * correctness'' even if it would be appropriate.
96  */
97 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
98 
99 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
100 
101 /*
102  * Optional return values, or out-arguments
103  */
104 #define SET_IF_NOT_NULL(obj, val) \
105 	if ((obj) != NULL) {      \
106 		*(obj) = (val);   \
107 	}
108 
109 /*%
110  * Get the allocation size for a struct with a flexible array member
111  * containing `count` elements. The struct is identified by a pointer,
112  * typically the one that points to (or will point to) the allocation.
113  */
114 #define STRUCT_FLEX_SIZE(pointer, member, count) \
115 	(sizeof(*(pointer)) + sizeof(*(pointer)->member) * (count))
116 
117 /*%
118  * Use this in translation units that would otherwise be empty, to
119  * suppress compiler warnings.
120  */
121 #define EMPTY_TRANSLATION_UNIT extern int isc__empty;
122 
123 /*%
124  * We use macros instead of calling the routines directly because
125  * the capital letters make the locking stand out.
126  */
127 
128 #ifdef ISC_UTIL_TRACEON
129 #define ISC_UTIL_TRACE(a) a
130 #include <stdio.h> /* Required for fprintf/stderr when tracing. */
131 #else		   /* ifdef ISC_UTIL_TRACEON */
132 #define ISC_UTIL_TRACE(a)
133 #endif /* ifdef ISC_UTIL_TRACEON */
134 
135 #include <isc/result.h> /* Contractual promise. */
136 
137 #define SPINLOCK(sp)                                                           \
138 	{                                                                      \
139 		ISC_UTIL_TRACE(fprintf(stderr, "SPINLOCKING %p %s %d\n", (sp), \
140 				       __FILE__, __LINE__));                   \
141 		isc_spinlock_lock((sp));                                       \
142 		ISC_UTIL_TRACE(fprintf(stderr, "SPINLOCKED %p %s %d\n", (sp),  \
143 				       __FILE__, __LINE__));                   \
144 	}
145 #define SPINUNLOCK(sp)                                                    \
146 	{                                                                 \
147 		isc_spinlock_unlock((sp));                                \
148 		ISC_UTIL_TRACE(fprintf(stderr, "SPINUNLOCKED %p %s %d\n", \
149 				       (sp), __FILE__, __LINE__));        \
150 	}
151 
152 #define LOCK(lp)                                                           \
153 	{                                                                  \
154 		ISC_UTIL_TRACE(fprintf(stderr, "LOCKING %p %s %d\n", (lp), \
155 				       __FILE__, __LINE__));               \
156 		isc_mutex_lock((lp));                                      \
157 		ISC_UTIL_TRACE(fprintf(stderr, "LOCKED %p %s %d\n", (lp),  \
158 				       __FILE__, __LINE__));               \
159 	}
160 #define UNLOCK(lp)                                                          \
161 	{                                                                   \
162 		isc_mutex_unlock((lp));                                     \
163 		ISC_UTIL_TRACE(fprintf(stderr, "UNLOCKED %p %s %d\n", (lp), \
164 				       __FILE__, __LINE__));                \
165 	}
166 
167 #define BROADCAST(cvp)                                                        \
168 	{                                                                     \
169 		ISC_UTIL_TRACE(fprintf(stderr, "BROADCAST %p %s %d\n", (cvp), \
170 				       __FILE__, __LINE__));                  \
171 		isc_condition_broadcast((cvp));                               \
172 	}
173 #define SIGNAL(cvp)                                                        \
174 	{                                                                  \
175 		ISC_UTIL_TRACE(fprintf(stderr, "SIGNAL %p %s %d\n", (cvp), \
176 				       __FILE__, __LINE__));               \
177 		isc_condition_signal((cvp));                               \
178 	}
179 #define WAIT(cvp, lp)                                                         \
180 	{                                                                     \
181 		ISC_UTIL_TRACE(fprintf(stderr, "WAIT %p LOCK %p %s %d\n",     \
182 				       (cvp), (lp), __FILE__, __LINE__));     \
183 		isc_condition_wait((cvp), (lp));                              \
184 		ISC_UTIL_TRACE(fprintf(stderr, "WAITED %p LOCKED %p %s %d\n", \
185 				       (cvp), (lp), __FILE__, __LINE__));     \
186 	}
187 
188 /*
189  * isc_condition_waituntil can return ISC_R_TIMEDOUT, so we
190  * don't RUNTIME_CHECK the result.
191  *
192  *  XXX Also, can't really debug this then...
193  */
194 
195 #define WAITUNTIL(cvp, lp, tp) isc_condition_waituntil((cvp), (lp), (tp))
196 
197 #define RWLOCK(lp, t)                                                         \
198 	{                                                                     \
199 		ISC_UTIL_TRACE(fprintf(stderr, "RWLOCK %p, %d %s %d\n", (lp), \
200 				       (t), __FILE__, __LINE__));             \
201 		isc_rwlock_lock((lp), (t));                                   \
202 		ISC_UTIL_TRACE(fprintf(stderr, "RWLOCKED %p, %d %s %d\n",     \
203 				       (lp), (t), __FILE__, __LINE__));       \
204 	}
205 #define RWUNLOCK(lp, t)                                                   \
206 	{                                                                 \
207 		ISC_UTIL_TRACE(fprintf(stderr, "RWUNLOCK %p, %d %s %d\n", \
208 				       (lp), (t), __FILE__, __LINE__));   \
209 		isc_rwlock_unlock((lp), (t));                             \
210 	}
211 
212 #define RDLOCK(lp)   RWLOCK(lp, isc_rwlocktype_read)
213 #define RDUNLOCK(lp) RWUNLOCK(lp, isc_rwlocktype_read)
214 #define WRLOCK(lp)   RWLOCK(lp, isc_rwlocktype_write)
215 #define WRUNLOCK(lp) RWUNLOCK(lp, isc_rwlocktype_write)
216 
217 #define UPGRADELOCK(lock, locktype)                                         \
218 	{                                                                   \
219 		if (locktype == isc_rwlocktype_read) {                      \
220 			if (isc_rwlock_tryupgrade(lock) == ISC_R_SUCCESS) { \
221 				locktype = isc_rwlocktype_write;            \
222 			} else {                                            \
223 				RWUNLOCK(lock, locktype);                   \
224 				locktype = isc_rwlocktype_write;            \
225 				RWLOCK(lock, locktype);                     \
226 			}                                                   \
227 		}                                                           \
228 		INSIST(locktype == isc_rwlocktype_write);                   \
229 	}
230 
231 /*
232  * List Macros.
233  */
234 #include <isc/list.h> /* Contractual promise. */
235 
236 #define LIST(type)		       ISC_LIST(type)
237 #define INIT_LIST(type)		       ISC_LIST_INIT(type)
238 #define LINK(type)		       ISC_LINK(type)
239 #define INIT_LINK(elt, link)	       ISC_LINK_INIT(elt, link)
240 #define HEAD(list)		       ISC_LIST_HEAD(list)
241 #define TAIL(list)		       ISC_LIST_TAIL(list)
242 #define EMPTY(list)		       ISC_LIST_EMPTY(list)
243 #define PREV(elt, link)		       ISC_LIST_PREV(elt, link)
244 #define NEXT(elt, link)		       ISC_LIST_NEXT(elt, link)
245 #define APPEND(list, elt, link)	       ISC_LIST_APPEND(list, elt, link)
246 #define PREPEND(list, elt, link)       ISC_LIST_PREPEND(list, elt, link)
247 #define UNLINK(list, elt, link)	       ISC_LIST_UNLINK(list, elt, link)
248 #define ENQUEUE(list, elt, link)       ISC_LIST_APPEND(list, elt, link)
249 #define DEQUEUE(list, elt, link)       ISC_LIST_UNLINK(list, elt, link)
250 #define INSERTBEFORE(li, b, e, ln)     ISC_LIST_INSERTBEFORE(li, b, e, ln)
251 #define INSERTAFTER(li, a, e, ln)      ISC_LIST_INSERTAFTER(li, a, e, ln)
252 #define APPENDLIST(list1, list2, link) ISC_LIST_APPENDLIST(list1, list2, link)
253 
254 /*%
255  * Performance
256  */
257 
258 /* GCC defines __SANITIZE_ADDRESS__, so reuse the macro for clang */
259 #if __has_feature(address_sanitizer)
260 #define __SANITIZE_ADDRESS__ 1
261 #endif /* if __has_feature(address_sanitizer) */
262 
263 #if __SANITIZE_ADDRESS__
264 #define ISC_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address")))
265 #else /* if __SANITIZE_ADDRESS__ */
266 #define ISC_NO_SANITIZE_ADDRESS
267 #endif /* if __SANITIZE_ADDRESS__ */
268 
269 #if __has_feature(thread_sanitizer)
270 #define __SANITIZE_THREAD__ 1
271 #endif /* if __has_feature(thread_sanitizer) */
272 
273 #if __SANITIZE_THREAD__
274 #define ISC_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
275 #else /* if __SANITIZE_THREAD__ */
276 #define ISC_NO_SANITIZE_THREAD
277 #endif /* if __SANITIZE_THREAD__ */
278 
279 #ifndef __lint__
280 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6)
281 #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
282 #elif __has_feature(c_static_assert)
283 #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
284 #else /* if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6) */
285 
286 /* Courtesy of Joseph Quinsey: https://godbolt.org/z/K9RvWS */
287 #define TOKENPASTE(a, b)	a##b /* "##" is the "Token Pasting Operator" */
288 #define EXPAND_THEN_PASTE(a, b) TOKENPASTE(a, b) /* expand then paste */
289 #define STATIC_ASSERT(x, msg) \
290 	enum { EXPAND_THEN_PASTE(ASSERT_line_, __LINE__) = 1 / ((msg) && (x)) }
291 #endif /* if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6) */
292 #else
293 #define STATIC_ASSERT(cond, msg)
294 #endif
295 
296 #ifdef UNIT_TESTING
297 extern void
298 mock_assert(const int result, const char *const expression,
299 	    const char *const file, const int line);
300 /*
301  *	Allow clang to determine that the following code is not reached
302  *	by calling abort() if the condition fails.  The abort() will
303  *	never be executed as mock_assert() and _assert_true() longjmp
304  *	or exit if the condition is false.
305  */
306 #define REQUIRE(expression)                                                   \
307 	((!(expression))                                                      \
308 		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
309 		 : (void)0)
310 #define ENSURE(expression)                                                    \
311 	((!(int)(expression))                                                 \
312 		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
313 		 : (void)0)
314 #define INSIST(expression)                                                    \
315 	((!(expression))                                                      \
316 		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
317 		 : (void)0)
318 #define INVARIANT(expression)                                                 \
319 	((!(expression))                                                      \
320 		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
321 		 : (void)0)
322 #define UNREACHABLE() \
323 	(mock_assert(0, "unreachable", __FILE__, __LINE__), abort())
324 #define _assert_true(c, e, f, l) \
325 	((c) ? (void)0 : (_assert_true(0, e, f, l), abort()))
326 #define _assert_int_equal(a, b, f, l) \
327 	(((a) == (b)) ? (void)0 : (_assert_int_equal(a, b, f, l), abort()))
328 #define _assert_int_not_equal(a, b, f, l) \
329 	(((a) != (b)) ? (void)0 : (_assert_int_not_equal(a, b, f, l), abort()))
330 #else			    /* UNIT_TESTING */
331 
332 /*
333  * Assertions
334  */
335 #include <isc/assertions.h> /* Contractual promise. */
336 
337 /*% Require Assertion */
338 #define REQUIRE(e)   ISC_REQUIRE(e)
339 /*% Ensure Assertion */
340 #define ENSURE(e)    ISC_ENSURE(e)
341 /*% Insist Assertion */
342 #define INSIST(e)    ISC_INSIST(e)
343 /*% Invariant Assertion */
344 #define INVARIANT(e) ISC_INVARIANT(e)
345 
346 #define UNREACHABLE() ISC_UNREACHABLE()
347 
348 #endif /* UNIT_TESTING */
349 
350 /*
351  * Errors
352  */
353 #include <errno.h> /* for errno */
354 
355 #include <isc/error.h>	/* Contractual promise. */
356 #include <isc/strerr.h> /* for ISC_STRERRORSIZE */
357 
358 #define UNEXPECTED_ERROR(...) \
359 	isc_error_unexpected(__FILE__, __LINE__, __func__, __VA_ARGS__)
360 
361 #define FATAL_ERROR(...) \
362 	isc_error_fatal(__FILE__, __LINE__, __func__, __VA_ARGS__)
363 
364 #define REPORT_SYSERROR(report, err, fmt, ...)                        \
365 	{                                                             \
366 		char strerr[ISC_STRERRORSIZE];                        \
367 		strerror_r(err, strerr, sizeof(strerr));              \
368 		report(__FILE__, __LINE__, __func__, fmt ": %s (%d)", \
369 		       ##__VA_ARGS__, strerr, err);                   \
370 	}
371 
372 #define UNEXPECTED_SYSERROR(err, ...) \
373 	REPORT_SYSERROR(isc_error_unexpected, err, __VA_ARGS__)
374 
375 #define FATAL_SYSERROR(err, ...) \
376 	REPORT_SYSERROR(isc_error_fatal, err, __VA_ARGS__)
377 
378 #ifdef UNIT_TESTING
379 
380 #define RUNTIME_CHECK(cond) \
381 	((cond) ? (void)0   \
382 		: (mock_assert(0, #cond, __FILE__, __LINE__), abort()))
383 
384 #else /* UNIT_TESTING */
385 
386 #define RUNTIME_CHECK(cond) \
387 	((cond) ? (void)0 : FATAL_ERROR("RUNTIME_CHECK(%s) failed", #cond))
388 
389 #endif /* UNIT_TESTING */
390 
391 /*%
392  * Runtime check which logs the error value returned by a POSIX Threads
393  * function and the error string that corresponds to it
394  */
395 #define PTHREADS_RUNTIME_CHECK(func, ret)           \
396 	if ((ret) != 0) {                           \
397 		FATAL_SYSERROR(ret, "%s()", #func); \
398 	}
399 
400 /*%
401  * Alignment
402  */
403 #ifdef __GNUC__
404 #define ISC_ALIGN(x, a) (((x) + (a) - 1) & ~((typeof(x))(a) - 1))
405 #else /* ifdef __GNUC__ */
406 #define ISC_ALIGN(x, a) (((x) + (a) - 1) & ~((uintmax_t)(a) - 1))
407 #endif /* ifdef __GNUC__ */
408 
409 /*%
410  * Swap
411  */
412 #define ISC_SWAP(a, b)                    \
413 	{                                 \
414 		typeof(a) __tmp_swap = a; \
415 		a = b;                    \
416 		b = __tmp_swap;           \
417 	}
418