xref: /freebsd/contrib/unbound/util/locks.c (revision 3005e0a300309ec169f8e42181b39865e910da6c)
1b7579f77SDag-Erling Smørgrav /**
2b7579f77SDag-Erling Smørgrav  * util/locks.c - unbound locking primitives
3b7579f77SDag-Erling Smørgrav  *
4b7579f77SDag-Erling Smørgrav  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5b7579f77SDag-Erling Smørgrav  *
6b7579f77SDag-Erling Smørgrav  * This software is open source.
7b7579f77SDag-Erling Smørgrav  *
8b7579f77SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
9b7579f77SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
10b7579f77SDag-Erling Smørgrav  * are met:
11b7579f77SDag-Erling Smørgrav  *
12b7579f77SDag-Erling Smørgrav  * Redistributions of source code must retain the above copyright notice,
13b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer.
14b7579f77SDag-Erling Smørgrav  *
15b7579f77SDag-Erling Smørgrav  * Redistributions in binary form must reproduce the above copyright notice,
16b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer in the documentation
17b7579f77SDag-Erling Smørgrav  * and/or other materials provided with the distribution.
18b7579f77SDag-Erling Smørgrav  *
19b7579f77SDag-Erling Smørgrav  * Neither the name of the NLNET LABS nor the names of its contributors may
20b7579f77SDag-Erling Smørgrav  * be used to endorse or promote products derived from this software without
21b7579f77SDag-Erling Smørgrav  * specific prior written permission.
22b7579f77SDag-Erling Smørgrav  *
23b7579f77SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2417d15b25SDag-Erling Smørgrav  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2517d15b25SDag-Erling Smørgrav  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2617d15b25SDag-Erling Smørgrav  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2717d15b25SDag-Erling Smørgrav  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2817d15b25SDag-Erling Smørgrav  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2917d15b25SDag-Erling Smørgrav  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
3017d15b25SDag-Erling Smørgrav  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3117d15b25SDag-Erling Smørgrav  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3217d15b25SDag-Erling Smørgrav  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3317d15b25SDag-Erling Smørgrav  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34b7579f77SDag-Erling Smørgrav  */
35b7579f77SDag-Erling Smørgrav 
36b7579f77SDag-Erling Smørgrav /**
37b7579f77SDag-Erling Smørgrav  * \file
38b7579f77SDag-Erling Smørgrav  * Implementation of locking and threading support.
39b7579f77SDag-Erling Smørgrav  * A place for locking debug code since most locking functions are macros.
40b7579f77SDag-Erling Smørgrav  */
41b7579f77SDag-Erling Smørgrav 
42b7579f77SDag-Erling Smørgrav #include "config.h"
43b7579f77SDag-Erling Smørgrav #include "util/locks.h"
44b7579f77SDag-Erling Smørgrav #include <signal.h>
45b7579f77SDag-Erling Smørgrav #ifdef HAVE_SYS_WAIT_H
46b7579f77SDag-Erling Smørgrav #include <sys/wait.h>
47b7579f77SDag-Erling Smørgrav #endif
48b7579f77SDag-Erling Smørgrav 
49b7579f77SDag-Erling Smørgrav /** block all signals, masks them away. */
50b7579f77SDag-Erling Smørgrav void
ub_thread_blocksigs(void)51b7579f77SDag-Erling Smørgrav ub_thread_blocksigs(void)
52b7579f77SDag-Erling Smørgrav {
53b7579f77SDag-Erling Smørgrav #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
54b7579f77SDag-Erling Smørgrav #  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
55b7579f77SDag-Erling Smørgrav 	int err;
56b7579f77SDag-Erling Smørgrav #  endif
57b7579f77SDag-Erling Smørgrav 	sigset_t sigset;
58b7579f77SDag-Erling Smørgrav 	sigfillset(&sigset);
59b7579f77SDag-Erling Smørgrav #ifdef HAVE_PTHREAD
60b7579f77SDag-Erling Smørgrav 	if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
61b7579f77SDag-Erling Smørgrav 		fatal_exit("pthread_sigmask: %s", strerror(err));
62b7579f77SDag-Erling Smørgrav #else
63b7579f77SDag-Erling Smørgrav #  ifdef HAVE_SOLARIS_THREADS
64b7579f77SDag-Erling Smørgrav 	if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
65b7579f77SDag-Erling Smørgrav 		fatal_exit("thr_sigsetmask: %s", strerror(err));
66b7579f77SDag-Erling Smørgrav #  else
67b7579f77SDag-Erling Smørgrav 	/* have nothing, do single process signal mask */
68b7579f77SDag-Erling Smørgrav 	if(sigprocmask(SIG_SETMASK, &sigset, NULL))
69b7579f77SDag-Erling Smørgrav 		fatal_exit("sigprocmask: %s", strerror(errno));
70b7579f77SDag-Erling Smørgrav #  endif /* HAVE_SOLARIS_THREADS */
71b7579f77SDag-Erling Smørgrav #endif /* HAVE_PTHREAD */
72b7579f77SDag-Erling Smørgrav #endif /* have signal stuff */
73b7579f77SDag-Erling Smørgrav }
74b7579f77SDag-Erling Smørgrav 
75b7579f77SDag-Erling Smørgrav /** unblock one signal, so we can catch it */
ub_thread_sig_unblock(int sig)76b7579f77SDag-Erling Smørgrav void ub_thread_sig_unblock(int sig)
77b7579f77SDag-Erling Smørgrav {
78b7579f77SDag-Erling Smørgrav #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
79b7579f77SDag-Erling Smørgrav #  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
80b7579f77SDag-Erling Smørgrav 	int err;
81b7579f77SDag-Erling Smørgrav #  endif
82b7579f77SDag-Erling Smørgrav 	sigset_t sigset;
83b7579f77SDag-Erling Smørgrav 	sigemptyset(&sigset);
84b7579f77SDag-Erling Smørgrav 	sigaddset(&sigset, sig);
85b7579f77SDag-Erling Smørgrav #ifdef HAVE_PTHREAD
86b7579f77SDag-Erling Smørgrav 	if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
87b7579f77SDag-Erling Smørgrav 		fatal_exit("pthread_sigmask: %s", strerror(err));
88b7579f77SDag-Erling Smørgrav #else
89b7579f77SDag-Erling Smørgrav #  ifdef HAVE_SOLARIS_THREADS
90b7579f77SDag-Erling Smørgrav 	if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
91b7579f77SDag-Erling Smørgrav 		fatal_exit("thr_sigsetmask: %s", strerror(err));
92b7579f77SDag-Erling Smørgrav #  else
93b7579f77SDag-Erling Smørgrav 	/* have nothing, do single thread case */
94b7579f77SDag-Erling Smørgrav 	if(sigprocmask(SIG_UNBLOCK, &sigset, NULL))
95b7579f77SDag-Erling Smørgrav 		fatal_exit("sigprocmask: %s", strerror(errno));
96b7579f77SDag-Erling Smørgrav #  endif /* HAVE_SOLARIS_THREADS */
97b7579f77SDag-Erling Smørgrav #endif /* HAVE_PTHREAD */
98b7579f77SDag-Erling Smørgrav #else
99b7579f77SDag-Erling Smørgrav 	(void)sig;
100b7579f77SDag-Erling Smørgrav #endif /* have signal stuff */
101b7579f77SDag-Erling Smørgrav }
102b7579f77SDag-Erling Smørgrav 
103b7579f77SDag-Erling Smørgrav #if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS)
104b7579f77SDag-Erling Smørgrav /**
105b7579f77SDag-Erling Smørgrav  * No threading available: fork a new process.
106b7579f77SDag-Erling Smørgrav  * This means no shared data structure, and no locking.
107b7579f77SDag-Erling Smørgrav  * Only the main thread ever returns. Exits on errors.
108b7579f77SDag-Erling Smørgrav  * @param thr: the location where to store the thread-id.
109b7579f77SDag-Erling Smørgrav  * @param func: function body of the thread. Return value of func is lost.
110b7579f77SDag-Erling Smørgrav  * @param arg: user argument to func.
111b7579f77SDag-Erling Smørgrav  */
112b7579f77SDag-Erling Smørgrav void
ub_thr_fork_create(ub_thread_type * thr,void * (* func)(void *),void * arg)113*3005e0a3SDag-Erling Smørgrav ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
114b7579f77SDag-Erling Smørgrav {
115b7579f77SDag-Erling Smørgrav 	pid_t pid = fork();
116b7579f77SDag-Erling Smørgrav 	switch(pid) {
117b7579f77SDag-Erling Smørgrav 	default:	/* main */
118*3005e0a3SDag-Erling Smørgrav 			*thr = (ub_thread_type)pid;
119b7579f77SDag-Erling Smørgrav 			return;
120b7579f77SDag-Erling Smørgrav 	case 0: 	/* child */
121*3005e0a3SDag-Erling Smørgrav 			*thr = (ub_thread_type)getpid();
122b7579f77SDag-Erling Smørgrav 			(void)(*func)(arg);
123b7579f77SDag-Erling Smørgrav 			exit(0);
124b7579f77SDag-Erling Smørgrav 	case -1:	/* error */
125b7579f77SDag-Erling Smørgrav 			fatal_exit("could not fork: %s", strerror(errno));
126b7579f77SDag-Erling Smørgrav 	}
127b7579f77SDag-Erling Smørgrav }
128b7579f77SDag-Erling Smørgrav 
129b7579f77SDag-Erling Smørgrav /**
130b7579f77SDag-Erling Smørgrav  * There is no threading. Wait for a process to terminate.
131*3005e0a3SDag-Erling Smørgrav  * Note that ub_thread_type is defined as pid_t.
132b7579f77SDag-Erling Smørgrav  * @param thread: the process id to wait for.
133b7579f77SDag-Erling Smørgrav  */
ub_thr_fork_wait(ub_thread_type thread)134*3005e0a3SDag-Erling Smørgrav void ub_thr_fork_wait(ub_thread_type thread)
135b7579f77SDag-Erling Smørgrav {
136b7579f77SDag-Erling Smørgrav 	int status = 0;
137b7579f77SDag-Erling Smørgrav 	if(waitpid((pid_t)thread, &status, 0) == -1)
138b7579f77SDag-Erling Smørgrav 		log_err("waitpid(%d): %s", (int)thread, strerror(errno));
139b7579f77SDag-Erling Smørgrav 	if(status != 0)
140b7579f77SDag-Erling Smørgrav 		log_warn("process %d abnormal exit with status %d",
141b7579f77SDag-Erling Smørgrav 			(int)thread, status);
142b7579f77SDag-Erling Smørgrav }
143b7579f77SDag-Erling Smørgrav #endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */
144b7579f77SDag-Erling Smørgrav 
145b7579f77SDag-Erling Smørgrav #ifdef HAVE_SOLARIS_THREADS
ub_thread_key_get(ub_thread_key_type key)146*3005e0a3SDag-Erling Smørgrav void* ub_thread_key_get(ub_thread_key_type key)
147b7579f77SDag-Erling Smørgrav {
148b7579f77SDag-Erling Smørgrav 	void* ret=NULL;
149b7579f77SDag-Erling Smørgrav 	LOCKRET(thr_getspecific(key, &ret));
150b7579f77SDag-Erling Smørgrav 	return ret;
151b7579f77SDag-Erling Smørgrav }
152b7579f77SDag-Erling Smørgrav #endif
153b7579f77SDag-Erling Smørgrav 
154b7579f77SDag-Erling Smørgrav #ifdef HAVE_WINDOWS_THREADS
155b7579f77SDag-Erling Smørgrav /** log a windows GetLastError message */
log_win_err(const char * str,DWORD err)156b7579f77SDag-Erling Smørgrav static void log_win_err(const char* str, DWORD err)
157b7579f77SDag-Erling Smørgrav {
158b7579f77SDag-Erling Smørgrav 	LPTSTR buf;
159b7579f77SDag-Erling Smørgrav 	if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
160b7579f77SDag-Erling Smørgrav 		FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
161b7579f77SDag-Erling Smørgrav 		NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) {
162b7579f77SDag-Erling Smørgrav 		/* could not format error message */
163b7579f77SDag-Erling Smørgrav 		log_err("%s, GetLastError=%d", str, (int)err);
164b7579f77SDag-Erling Smørgrav 		return;
165b7579f77SDag-Erling Smørgrav 	}
166b7579f77SDag-Erling Smørgrav 	log_err("%s, (err=%d): %s", str, (int)err, buf);
167b7579f77SDag-Erling Smørgrav 	LocalFree(buf);
168b7579f77SDag-Erling Smørgrav }
169b7579f77SDag-Erling Smørgrav 
lock_basic_init(lock_basic_type * lock)170*3005e0a3SDag-Erling Smørgrav void lock_basic_init(lock_basic_type* lock)
171b7579f77SDag-Erling Smørgrav {
172b7579f77SDag-Erling Smørgrav 	/* implement own lock, because windows HANDLE as Mutex usage
173b7579f77SDag-Erling Smørgrav 	 * uses too many handles and would bog down the whole system. */
174b7579f77SDag-Erling Smørgrav 	(void)InterlockedExchange(lock, 0);
175b7579f77SDag-Erling Smørgrav }
176b7579f77SDag-Erling Smørgrav 
lock_basic_destroy(lock_basic_type * lock)177*3005e0a3SDag-Erling Smørgrav void lock_basic_destroy(lock_basic_type* lock)
178b7579f77SDag-Erling Smørgrav {
179b7579f77SDag-Erling Smørgrav 	(void)InterlockedExchange(lock, 0);
180b7579f77SDag-Erling Smørgrav }
181b7579f77SDag-Erling Smørgrav 
lock_basic_lock(lock_basic_type * lock)182*3005e0a3SDag-Erling Smørgrav void lock_basic_lock(lock_basic_type* lock)
183b7579f77SDag-Erling Smørgrav {
184b7579f77SDag-Erling Smørgrav 	LONG wait = 1; /* wait 1 msec at first */
185b7579f77SDag-Erling Smørgrav 
186b7579f77SDag-Erling Smørgrav 	while(InterlockedExchange(lock, 1)) {
187b7579f77SDag-Erling Smørgrav 		/* if the old value was 1 then if was already locked */
188b7579f77SDag-Erling Smørgrav 		Sleep(wait); /* wait with sleep */
189b7579f77SDag-Erling Smørgrav 		wait *= 2;   /* exponential backoff for waiting */
190b7579f77SDag-Erling Smørgrav 	}
191b7579f77SDag-Erling Smørgrav 	/* the old value was 0, but we inserted 1, we locked it! */
192b7579f77SDag-Erling Smørgrav }
193b7579f77SDag-Erling Smørgrav 
lock_basic_unlock(lock_basic_type * lock)194*3005e0a3SDag-Erling Smørgrav void lock_basic_unlock(lock_basic_type* lock)
195b7579f77SDag-Erling Smørgrav {
196b7579f77SDag-Erling Smørgrav 	/* unlock it by inserting the value of 0. xchg for cache coherency. */
197b7579f77SDag-Erling Smørgrav 	(void)InterlockedExchange(lock, 0);
198b7579f77SDag-Erling Smørgrav }
199b7579f77SDag-Erling Smørgrav 
ub_thread_key_create(ub_thread_key_type * key,void * f)200*3005e0a3SDag-Erling Smørgrav void ub_thread_key_create(ub_thread_key_type* key, void* f)
201b7579f77SDag-Erling Smørgrav {
202b7579f77SDag-Erling Smørgrav 	*key = TlsAlloc();
203b7579f77SDag-Erling Smørgrav 	if(*key == TLS_OUT_OF_INDEXES) {
204b7579f77SDag-Erling Smørgrav 		*key = 0;
205b7579f77SDag-Erling Smørgrav 		log_win_err("TlsAlloc Failed(OUT_OF_INDEXES)", GetLastError());
206b7579f77SDag-Erling Smørgrav 	}
207b7579f77SDag-Erling Smørgrav 	else ub_thread_key_set(*key, f);
208b7579f77SDag-Erling Smørgrav }
209b7579f77SDag-Erling Smørgrav 
ub_thread_key_set(ub_thread_key_type key,void * v)210*3005e0a3SDag-Erling Smørgrav void ub_thread_key_set(ub_thread_key_type key, void* v)
211b7579f77SDag-Erling Smørgrav {
212b7579f77SDag-Erling Smørgrav 	if(!TlsSetValue(key, v)) {
213b7579f77SDag-Erling Smørgrav 		log_win_err("TlsSetValue failed", GetLastError());
214b7579f77SDag-Erling Smørgrav 	}
215b7579f77SDag-Erling Smørgrav }
216b7579f77SDag-Erling Smørgrav 
ub_thread_key_get(ub_thread_key_type key)217*3005e0a3SDag-Erling Smørgrav void* ub_thread_key_get(ub_thread_key_type key)
218b7579f77SDag-Erling Smørgrav {
219b7579f77SDag-Erling Smørgrav 	void* ret = (void*)TlsGetValue(key);
220b7579f77SDag-Erling Smørgrav 	if(ret == NULL && GetLastError() != ERROR_SUCCESS) {
221b7579f77SDag-Erling Smørgrav 		log_win_err("TlsGetValue failed", GetLastError());
222b7579f77SDag-Erling Smørgrav 	}
223b7579f77SDag-Erling Smørgrav 	return ret;
224b7579f77SDag-Erling Smørgrav }
225b7579f77SDag-Erling Smørgrav 
ub_thread_create(ub_thread_type * thr,void * (* func)(void *),void * arg)226*3005e0a3SDag-Erling Smørgrav void ub_thread_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
227b7579f77SDag-Erling Smørgrav {
228b7579f77SDag-Erling Smørgrav #ifndef HAVE__BEGINTHREADEX
229b7579f77SDag-Erling Smørgrav 	*thr = CreateThread(NULL, /* default security (no inherit handle) */
230b7579f77SDag-Erling Smørgrav 		0, /* default stack size */
231b7579f77SDag-Erling Smørgrav 		(LPTHREAD_START_ROUTINE)func, arg,
232b7579f77SDag-Erling Smørgrav 		0, /* default flags, run immediately */
233b7579f77SDag-Erling Smørgrav 		NULL); /* do not store thread identifier anywhere */
234b7579f77SDag-Erling Smørgrav #else
23505ab2901SDag-Erling Smørgrav 	/* the beginthreadex routine setups for the C lib; aligns stack */
236*3005e0a3SDag-Erling Smørgrav 	*thr=(ub_thread_type)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL);
237b7579f77SDag-Erling Smørgrav #endif
238b7579f77SDag-Erling Smørgrav 	if(*thr == NULL) {
239b7579f77SDag-Erling Smørgrav 		log_win_err("CreateThread failed", GetLastError());
240b7579f77SDag-Erling Smørgrav 		fatal_exit("thread create failed");
241b7579f77SDag-Erling Smørgrav 	}
242b7579f77SDag-Erling Smørgrav }
243b7579f77SDag-Erling Smørgrav 
ub_thread_self(void)244*3005e0a3SDag-Erling Smørgrav ub_thread_type ub_thread_self(void)
245b7579f77SDag-Erling Smørgrav {
246b7579f77SDag-Erling Smørgrav 	return GetCurrentThread();
247b7579f77SDag-Erling Smørgrav }
248b7579f77SDag-Erling Smørgrav 
ub_thread_join(ub_thread_type thr)249*3005e0a3SDag-Erling Smørgrav void ub_thread_join(ub_thread_type thr)
250b7579f77SDag-Erling Smørgrav {
251b7579f77SDag-Erling Smørgrav 	DWORD ret = WaitForSingleObject(thr, INFINITE);
252b7579f77SDag-Erling Smørgrav 	if(ret == WAIT_FAILED) {
253b7579f77SDag-Erling Smørgrav 		log_win_err("WaitForSingleObject(Thread):WAIT_FAILED",
254b7579f77SDag-Erling Smørgrav 			GetLastError());
255b7579f77SDag-Erling Smørgrav 	} else if(ret == WAIT_TIMEOUT) {
256b7579f77SDag-Erling Smørgrav 		log_win_err("WaitForSingleObject(Thread):WAIT_TIMEOUT",
257b7579f77SDag-Erling Smørgrav 			GetLastError());
258b7579f77SDag-Erling Smørgrav 	}
259b7579f77SDag-Erling Smørgrav 	/* and close the handle to the thread */
260b7579f77SDag-Erling Smørgrav 	if(!CloseHandle(thr)) {
261b7579f77SDag-Erling Smørgrav 		log_win_err("CloseHandle(Thread) failed", GetLastError());
262b7579f77SDag-Erling Smørgrav 	}
263b7579f77SDag-Erling Smørgrav }
264b7579f77SDag-Erling Smørgrav #endif /* HAVE_WINDOWS_THREADS */
265