xref: /netbsd-src/external/bsd/unbound/dist/util/locks.c (revision 0cd9f4ecf44538bbdd5619b5b2081449960ab3e6)
13b6c3722Schristos /**
23b6c3722Schristos  * util/locks.c - unbound locking primitives
33b6c3722Schristos  *
43b6c3722Schristos  * Copyright (c) 2007, NLnet Labs. All rights reserved.
53b6c3722Schristos  *
63b6c3722Schristos  * This software is open source.
73b6c3722Schristos  *
83b6c3722Schristos  * Redistribution and use in source and binary forms, with or without
93b6c3722Schristos  * modification, are permitted provided that the following conditions
103b6c3722Schristos  * are met:
113b6c3722Schristos  *
123b6c3722Schristos  * Redistributions of source code must retain the above copyright notice,
133b6c3722Schristos  * this list of conditions and the following disclaimer.
143b6c3722Schristos  *
153b6c3722Schristos  * Redistributions in binary form must reproduce the above copyright notice,
163b6c3722Schristos  * this list of conditions and the following disclaimer in the documentation
173b6c3722Schristos  * and/or other materials provided with the distribution.
183b6c3722Schristos  *
193b6c3722Schristos  * Neither the name of the NLNET LABS nor the names of its contributors may
203b6c3722Schristos  * be used to endorse or promote products derived from this software without
213b6c3722Schristos  * specific prior written permission.
223b6c3722Schristos  *
233b6c3722Schristos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
243b6c3722Schristos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
253b6c3722Schristos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
263b6c3722Schristos  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
273b6c3722Schristos  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
283b6c3722Schristos  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
293b6c3722Schristos  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
303b6c3722Schristos  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
313b6c3722Schristos  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
323b6c3722Schristos  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
333b6c3722Schristos  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
343b6c3722Schristos  */
353b6c3722Schristos 
363b6c3722Schristos /**
373b6c3722Schristos  * \file
383b6c3722Schristos  * Implementation of locking and threading support.
393b6c3722Schristos  * A place for locking debug code since most locking functions are macros.
403b6c3722Schristos  */
413b6c3722Schristos 
423b6c3722Schristos #include "config.h"
433b6c3722Schristos #include "util/locks.h"
443b6c3722Schristos #include <signal.h>
453b6c3722Schristos #ifdef HAVE_SYS_WAIT_H
463b6c3722Schristos #include <sys/wait.h>
473b6c3722Schristos #endif
483b6c3722Schristos 
493b6c3722Schristos /** block all signals, masks them away. */
503b6c3722Schristos void
ub_thread_blocksigs(void)513b6c3722Schristos ub_thread_blocksigs(void)
523b6c3722Schristos {
533b6c3722Schristos #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
543b6c3722Schristos #  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
553b6c3722Schristos 	int err;
563b6c3722Schristos #  endif
573b6c3722Schristos 	sigset_t sigset;
583b6c3722Schristos 	sigfillset(&sigset);
593b6c3722Schristos #ifdef HAVE_PTHREAD
603b6c3722Schristos 	if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
613b6c3722Schristos 		fatal_exit("pthread_sigmask: %s", strerror(err));
623b6c3722Schristos #else
633b6c3722Schristos #  ifdef HAVE_SOLARIS_THREADS
643b6c3722Schristos 	if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
653b6c3722Schristos 		fatal_exit("thr_sigsetmask: %s", strerror(err));
663b6c3722Schristos #  else
673b6c3722Schristos 	/* have nothing, do single process signal mask */
683b6c3722Schristos 	if(sigprocmask(SIG_SETMASK, &sigset, NULL))
693b6c3722Schristos 		fatal_exit("sigprocmask: %s", strerror(errno));
703b6c3722Schristos #  endif /* HAVE_SOLARIS_THREADS */
713b6c3722Schristos #endif /* HAVE_PTHREAD */
723b6c3722Schristos #endif /* have signal stuff */
733b6c3722Schristos }
743b6c3722Schristos 
753b6c3722Schristos /** unblock one signal, so we can catch it */
ub_thread_sig_unblock(int sig)763b6c3722Schristos void ub_thread_sig_unblock(int sig)
773b6c3722Schristos {
783b6c3722Schristos #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
793b6c3722Schristos #  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
803b6c3722Schristos 	int err;
813b6c3722Schristos #  endif
823b6c3722Schristos 	sigset_t sigset;
833b6c3722Schristos 	sigemptyset(&sigset);
843b6c3722Schristos 	sigaddset(&sigset, sig);
853b6c3722Schristos #ifdef HAVE_PTHREAD
863b6c3722Schristos 	if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
873b6c3722Schristos 		fatal_exit("pthread_sigmask: %s", strerror(err));
883b6c3722Schristos #else
893b6c3722Schristos #  ifdef HAVE_SOLARIS_THREADS
903b6c3722Schristos 	if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
913b6c3722Schristos 		fatal_exit("thr_sigsetmask: %s", strerror(err));
923b6c3722Schristos #  else
933b6c3722Schristos 	/* have nothing, do single thread case */
943b6c3722Schristos 	if(sigprocmask(SIG_UNBLOCK, &sigset, NULL))
953b6c3722Schristos 		fatal_exit("sigprocmask: %s", strerror(errno));
963b6c3722Schristos #  endif /* HAVE_SOLARIS_THREADS */
973b6c3722Schristos #endif /* HAVE_PTHREAD */
983b6c3722Schristos #else
993b6c3722Schristos 	(void)sig;
1003b6c3722Schristos #endif /* have signal stuff */
1013b6c3722Schristos }
1023b6c3722Schristos 
1033b6c3722Schristos #if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS)
1043b6c3722Schristos /**
1053b6c3722Schristos  * No threading available: fork a new process.
1063b6c3722Schristos  * This means no shared data structure, and no locking.
1073b6c3722Schristos  * Only the main thread ever returns. Exits on errors.
1083b6c3722Schristos  * @param thr: the location where to store the thread-id.
1093b6c3722Schristos  * @param func: function body of the thread. Return value of func is lost.
1103b6c3722Schristos  * @param arg: user argument to func.
1113b6c3722Schristos  */
1123b6c3722Schristos void
ub_thr_fork_create(ub_thread_type * thr,void * (* func)(void *),void * arg)113*0cd9f4ecSchristos ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
1143b6c3722Schristos {
1153b6c3722Schristos 	pid_t pid = fork();
1163b6c3722Schristos 	switch(pid) {
1173b6c3722Schristos 	default:	/* main */
118*0cd9f4ecSchristos 			*thr = (ub_thread_type)pid;
1193b6c3722Schristos 			return;
1203b6c3722Schristos 	case 0: 	/* child */
121*0cd9f4ecSchristos 			*thr = (ub_thread_type)getpid();
1223b6c3722Schristos 			(void)(*func)(arg);
1233b6c3722Schristos 			exit(0);
1243b6c3722Schristos 	case -1:	/* error */
1253b6c3722Schristos 			fatal_exit("could not fork: %s", strerror(errno));
1263b6c3722Schristos 	}
1273b6c3722Schristos }
1283b6c3722Schristos 
1293b6c3722Schristos /**
1303b6c3722Schristos  * There is no threading. Wait for a process to terminate.
131*0cd9f4ecSchristos  * Note that ub_thread_type is defined as pid_t.
1323b6c3722Schristos  * @param thread: the process id to wait for.
1333b6c3722Schristos  */
ub_thr_fork_wait(ub_thread_type thread)134*0cd9f4ecSchristos void ub_thr_fork_wait(ub_thread_type thread)
1353b6c3722Schristos {
1363b6c3722Schristos 	int status = 0;
1373b6c3722Schristos 	if(waitpid((pid_t)thread, &status, 0) == -1)
1383b6c3722Schristos 		log_err("waitpid(%d): %s", (int)thread, strerror(errno));
1393b6c3722Schristos 	if(status != 0)
1403b6c3722Schristos 		log_warn("process %d abnormal exit with status %d",
1413b6c3722Schristos 			(int)thread, status);
1423b6c3722Schristos }
1433b6c3722Schristos #endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */
1443b6c3722Schristos 
1453b6c3722Schristos #ifdef HAVE_SOLARIS_THREADS
ub_thread_key_get(ub_thread_key_type key)146*0cd9f4ecSchristos void* ub_thread_key_get(ub_thread_key_type key)
1473b6c3722Schristos {
1483b6c3722Schristos 	void* ret=NULL;
1493b6c3722Schristos 	LOCKRET(thr_getspecific(key, &ret));
1503b6c3722Schristos 	return ret;
1513b6c3722Schristos }
1523b6c3722Schristos #endif
1533b6c3722Schristos 
1543b6c3722Schristos #ifdef HAVE_WINDOWS_THREADS
1553b6c3722Schristos /** log a windows GetLastError message */
log_win_err(const char * str,DWORD err)1563b6c3722Schristos static void log_win_err(const char* str, DWORD err)
1573b6c3722Schristos {
1583b6c3722Schristos 	LPTSTR buf;
1593b6c3722Schristos 	if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1603b6c3722Schristos 		FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
1613b6c3722Schristos 		NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) {
1623b6c3722Schristos 		/* could not format error message */
1633b6c3722Schristos 		log_err("%s, GetLastError=%d", str, (int)err);
1643b6c3722Schristos 		return;
1653b6c3722Schristos 	}
1663b6c3722Schristos 	log_err("%s, (err=%d): %s", str, (int)err, buf);
1673b6c3722Schristos 	LocalFree(buf);
1683b6c3722Schristos }
1693b6c3722Schristos 
lock_basic_init(lock_basic_type * lock)170*0cd9f4ecSchristos void lock_basic_init(lock_basic_type* lock)
1713b6c3722Schristos {
1723b6c3722Schristos 	/* implement own lock, because windows HANDLE as Mutex usage
1733b6c3722Schristos 	 * uses too many handles and would bog down the whole system. */
1743b6c3722Schristos 	(void)InterlockedExchange(lock, 0);
1753b6c3722Schristos }
1763b6c3722Schristos 
lock_basic_destroy(lock_basic_type * lock)177*0cd9f4ecSchristos void lock_basic_destroy(lock_basic_type* lock)
1783b6c3722Schristos {
1793b6c3722Schristos 	(void)InterlockedExchange(lock, 0);
1803b6c3722Schristos }
1813b6c3722Schristos 
lock_basic_lock(lock_basic_type * lock)182*0cd9f4ecSchristos void lock_basic_lock(lock_basic_type* lock)
1833b6c3722Schristos {
1843b6c3722Schristos 	LONG wait = 1; /* wait 1 msec at first */
1853b6c3722Schristos 
1863b6c3722Schristos 	while(InterlockedExchange(lock, 1)) {
1873b6c3722Schristos 		/* if the old value was 1 then if was already locked */
1883b6c3722Schristos 		Sleep(wait); /* wait with sleep */
1893b6c3722Schristos 		wait *= 2;   /* exponential backoff for waiting */
1903b6c3722Schristos 	}
1913b6c3722Schristos 	/* the old value was 0, but we inserted 1, we locked it! */
1923b6c3722Schristos }
1933b6c3722Schristos 
lock_basic_unlock(lock_basic_type * lock)194*0cd9f4ecSchristos void lock_basic_unlock(lock_basic_type* lock)
1953b6c3722Schristos {
1963b6c3722Schristos 	/* unlock it by inserting the value of 0. xchg for cache coherency. */
1973b6c3722Schristos 	(void)InterlockedExchange(lock, 0);
1983b6c3722Schristos }
1993b6c3722Schristos 
ub_thread_key_create(ub_thread_key_type * key,void * f)200*0cd9f4ecSchristos void ub_thread_key_create(ub_thread_key_type* key, void* f)
2013b6c3722Schristos {
2023b6c3722Schristos 	*key = TlsAlloc();
2033b6c3722Schristos 	if(*key == TLS_OUT_OF_INDEXES) {
2043b6c3722Schristos 		*key = 0;
2053b6c3722Schristos 		log_win_err("TlsAlloc Failed(OUT_OF_INDEXES)", GetLastError());
2063b6c3722Schristos 	}
2073b6c3722Schristos 	else ub_thread_key_set(*key, f);
2083b6c3722Schristos }
2093b6c3722Schristos 
ub_thread_key_set(ub_thread_key_type key,void * v)210*0cd9f4ecSchristos void ub_thread_key_set(ub_thread_key_type key, void* v)
2113b6c3722Schristos {
2123b6c3722Schristos 	if(!TlsSetValue(key, v)) {
2133b6c3722Schristos 		log_win_err("TlsSetValue failed", GetLastError());
2143b6c3722Schristos 	}
2153b6c3722Schristos }
2163b6c3722Schristos 
ub_thread_key_get(ub_thread_key_type key)217*0cd9f4ecSchristos void* ub_thread_key_get(ub_thread_key_type key)
2183b6c3722Schristos {
2193b6c3722Schristos 	void* ret = (void*)TlsGetValue(key);
2203b6c3722Schristos 	if(ret == NULL && GetLastError() != ERROR_SUCCESS) {
2213b6c3722Schristos 		log_win_err("TlsGetValue failed", GetLastError());
2223b6c3722Schristos 	}
2233b6c3722Schristos 	return ret;
2243b6c3722Schristos }
2253b6c3722Schristos 
ub_thread_create(ub_thread_type * thr,void * (* func)(void *),void * arg)226*0cd9f4ecSchristos void ub_thread_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
2273b6c3722Schristos {
2283b6c3722Schristos #ifndef HAVE__BEGINTHREADEX
2293b6c3722Schristos 	*thr = CreateThread(NULL, /* default security (no inherit handle) */
2303b6c3722Schristos 		0, /* default stack size */
2313b6c3722Schristos 		(LPTHREAD_START_ROUTINE)func, arg,
2323b6c3722Schristos 		0, /* default flags, run immediately */
2333b6c3722Schristos 		NULL); /* do not store thread identifier anywhere */
2343b6c3722Schristos #else
2353b6c3722Schristos 	/* the beginthreadex routine setups for the C lib; aligns stack */
236*0cd9f4ecSchristos 	*thr=(ub_thread_type)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL);
2373b6c3722Schristos #endif
2383b6c3722Schristos 	if(*thr == NULL) {
2393b6c3722Schristos 		log_win_err("CreateThread failed", GetLastError());
2403b6c3722Schristos 		fatal_exit("thread create failed");
2413b6c3722Schristos 	}
2423b6c3722Schristos }
2433b6c3722Schristos 
ub_thread_self(void)244*0cd9f4ecSchristos ub_thread_type ub_thread_self(void)
2453b6c3722Schristos {
2463b6c3722Schristos 	return GetCurrentThread();
2473b6c3722Schristos }
2483b6c3722Schristos 
ub_thread_join(ub_thread_type thr)249*0cd9f4ecSchristos void ub_thread_join(ub_thread_type thr)
2503b6c3722Schristos {
2513b6c3722Schristos 	DWORD ret = WaitForSingleObject(thr, INFINITE);
2523b6c3722Schristos 	if(ret == WAIT_FAILED) {
2533b6c3722Schristos 		log_win_err("WaitForSingleObject(Thread):WAIT_FAILED",
2543b6c3722Schristos 			GetLastError());
2553b6c3722Schristos 	} else if(ret == WAIT_TIMEOUT) {
2563b6c3722Schristos 		log_win_err("WaitForSingleObject(Thread):WAIT_TIMEOUT",
2573b6c3722Schristos 			GetLastError());
2583b6c3722Schristos 	}
2593b6c3722Schristos 	/* and close the handle to the thread */
2603b6c3722Schristos 	if(!CloseHandle(thr)) {
2613b6c3722Schristos 		log_win_err("CloseHandle(Thread) failed", GetLastError());
2623b6c3722Schristos 	}
2633b6c3722Schristos }
2643b6c3722Schristos #endif /* HAVE_WINDOWS_THREADS */
265