xref: /netbsd-src/external/mpl/bind/dist/lib/isc/thread.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: thread.c,v 1.3 2025/01/26 16:25:39 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 /*! \file */
17 
18 #if defined(HAVE_SCHED_H)
19 #include <sched.h>
20 #endif /* if defined(HAVE_SCHED_H) */
21 
22 #if defined(HAVE_CPUSET_H)
23 #include <sys/cpuset.h>
24 #include <sys/param.h>
25 #endif /* if defined(HAVE_CPUSET_H) */
26 
27 #if defined(HAVE_SYS_PROCSET_H)
28 #include <sys/processor.h>
29 #include <sys/procset.h>
30 #include <sys/types.h>
31 #endif /* if defined(HAVE_SYS_PROCSET_H) */
32 
33 #include <stdlib.h>
34 
35 #include <isc/atomic.h>
36 #include <isc/iterated_hash.h>
37 #include <isc/strerr.h>
38 #include <isc/thread.h>
39 #include <isc/tid.h>
40 #include <isc/urcu.h>
41 #include <isc/util.h>
42 
43 #ifndef THREAD_MINSTACKSIZE
44 #define THREAD_MINSTACKSIZE (1024U * 1024)
45 #endif /* ifndef THREAD_MINSTACKSIZE */
46 
47 /*
48  * We can't use isc_mem API here, because it's called too early and the
49  * isc_mem_debugging flags can be changed later causing mismatch between flags
50  * used for isc_mem_get() and isc_mem_put().
51  */
52 
53 struct thread_wrap {
54 	struct rcu_head rcu_head;
55 	isc_threadfunc_t func;
56 	void *arg;
57 };
58 
59 static struct thread_wrap *
60 thread_wrap(isc_threadfunc_t func, void *arg) {
61 	struct thread_wrap *wrap = malloc(sizeof(*wrap));
62 	RUNTIME_CHECK(wrap != NULL);
63 	*wrap = (struct thread_wrap){
64 		.func = func,
65 		.arg = arg,
66 	};
67 	return wrap;
68 }
69 
70 static void *
71 thread_body(struct thread_wrap *wrap) {
72 	isc_threadfunc_t func = wrap->func;
73 	void *arg = wrap->arg;
74 	void *ret = NULL;
75 
76 	/*
77 	 * Every thread starts with a malloc() call to prevent memory bloat
78 	 * caused by a jemalloc quirk.  We use CMM_ACCESS_ONCE() To stop an
79 	 * optimizing compiler from stripping out free(malloc(1)).
80 	 */
81 	void *jemalloc_enforce_init = NULL;
82 	CMM_ACCESS_ONCE(jemalloc_enforce_init) = malloc(1);
83 	free(jemalloc_enforce_init);
84 
85 	free(wrap);
86 
87 	ret = func(arg);
88 
89 	return ret;
90 }
91 
92 static void *
93 thread_run(void *wrap) {
94 	/*
95 	 * Get a thread-local digest context only in new threads.
96 	 * The main thread is handled by isc__initialize().
97 	 */
98 	isc__iterated_hash_initialize();
99 
100 	rcu_register_thread();
101 
102 	void *ret = thread_body(wrap);
103 
104 	isc__iterated_hash_shutdown();
105 
106 	rcu_unregister_thread();
107 
108 	return ret;
109 }
110 
111 void
112 isc_thread_main(isc_threadfunc_t func, void *arg) {
113 	/*
114 	 * Either this thread has not yet been started, so it can become the
115 	 * main thread, or it has already been annointed as the chosen zero
116 	 */
117 	REQUIRE(isc_tid() == ISC_TID_UNKNOWN || isc_tid() == 0);
118 	thread_body(thread_wrap(func, arg));
119 }
120 
121 void
122 isc_thread_create(isc_threadfunc_t func, void *arg, isc_thread_t *thread) {
123 	int ret;
124 	pthread_attr_t attr;
125 
126 	pthread_attr_init(&attr);
127 
128 #if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
129 	defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE)
130 	size_t stacksize;
131 	ret = pthread_attr_getstacksize(&attr, &stacksize);
132 	PTHREADS_RUNTIME_CHECK(pthread_attr_getstacksize, ret);
133 
134 	if (stacksize < THREAD_MINSTACKSIZE) {
135 		ret = pthread_attr_setstacksize(&attr, THREAD_MINSTACKSIZE);
136 		PTHREADS_RUNTIME_CHECK(pthread_attr_setstacksize, ret);
137 	}
138 #endif /* if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
139 	* defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) */
140 
141 	ret = pthread_create(thread, &attr, thread_run, thread_wrap(func, arg));
142 	PTHREADS_RUNTIME_CHECK(pthread_create, ret);
143 
144 	pthread_attr_destroy(&attr);
145 }
146 
147 void
148 isc_thread_join(isc_thread_t thread, void **resultp) {
149 	int ret = pthread_join(thread, resultp);
150 
151 	PTHREADS_RUNTIME_CHECK(pthread_join, ret);
152 }
153 
154 void
155 isc_thread_setname(isc_thread_t thread, const char *name) {
156 #if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__)
157 	/*
158 	 * macOS has pthread_setname_np but only works on the
159 	 * current thread so it's not used here
160 	 */
161 #if defined(__NetBSD__)
162 	(void)pthread_setname_np(thread, name, NULL);
163 #else  /* if defined(__NetBSD__) */
164 	(void)pthread_setname_np(thread, name);
165 #endif /* if defined(__NetBSD__) */
166 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
167 	(void)pthread_set_name_np(thread, name);
168 #else  /* if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__) */
169 	UNUSED(thread);
170 	UNUSED(name);
171 #endif /* if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__) */
172 }
173 
174 void
175 isc_thread_yield(void) {
176 #if defined(HAVE_SCHED_YIELD)
177 	sched_yield();
178 #elif defined(HAVE_PTHREAD_YIELD)
179 	pthread_yield();
180 #elif defined(HAVE_PTHREAD_YIELD_NP)
181 	pthread_yield_np();
182 #endif /* if defined(HAVE_SCHED_YIELD) */
183 }
184