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