Home | History | Annotate | Line # | Download | only in isc
      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