xref: /openbsd-src/usr.sbin/unbound/util/locks.c (revision 77079be7e74cd516ce5a60021c2a03ba383e594d)
1933707f3Ssthen /**
2933707f3Ssthen  * util/locks.c - unbound locking primitives
3933707f3Ssthen  *
4933707f3Ssthen  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5933707f3Ssthen  *
6933707f3Ssthen  * This software is open source.
7933707f3Ssthen  *
8933707f3Ssthen  * Redistribution and use in source and binary forms, with or without
9933707f3Ssthen  * modification, are permitted provided that the following conditions
10933707f3Ssthen  * are met:
11933707f3Ssthen  *
12933707f3Ssthen  * Redistributions of source code must retain the above copyright notice,
13933707f3Ssthen  * this list of conditions and the following disclaimer.
14933707f3Ssthen  *
15933707f3Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16933707f3Ssthen  * this list of conditions and the following disclaimer in the documentation
17933707f3Ssthen  * and/or other materials provided with the distribution.
18933707f3Ssthen  *
19933707f3Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20933707f3Ssthen  * be used to endorse or promote products derived from this software without
21933707f3Ssthen  * specific prior written permission.
22933707f3Ssthen  *
23933707f3Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
245d76a658Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
255d76a658Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
265d76a658Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
275d76a658Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
285d76a658Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
295d76a658Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
305d76a658Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
315d76a658Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
325d76a658Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
335d76a658Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34933707f3Ssthen  */
35933707f3Ssthen 
36933707f3Ssthen /**
37933707f3Ssthen  * \file
38933707f3Ssthen  * Implementation of locking and threading support.
39933707f3Ssthen  * A place for locking debug code since most locking functions are macros.
40933707f3Ssthen  */
41933707f3Ssthen 
42933707f3Ssthen #include "config.h"
43933707f3Ssthen #include "util/locks.h"
44933707f3Ssthen #include <signal.h>
45933707f3Ssthen #ifdef HAVE_SYS_WAIT_H
46933707f3Ssthen #include <sys/wait.h>
47933707f3Ssthen #endif
48933707f3Ssthen 
49933707f3Ssthen /** block all signals, masks them away. */
50933707f3Ssthen void
ub_thread_blocksigs(void)51933707f3Ssthen ub_thread_blocksigs(void)
52933707f3Ssthen {
53933707f3Ssthen #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
54933707f3Ssthen #  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
55933707f3Ssthen 	int err;
56933707f3Ssthen #  endif
57933707f3Ssthen 	sigset_t sigset;
58933707f3Ssthen 	sigfillset(&sigset);
59933707f3Ssthen #ifdef HAVE_PTHREAD
60933707f3Ssthen 	if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
61933707f3Ssthen 		fatal_exit("pthread_sigmask: %s", strerror(err));
62933707f3Ssthen #else
63933707f3Ssthen #  ifdef HAVE_SOLARIS_THREADS
64933707f3Ssthen 	if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
65933707f3Ssthen 		fatal_exit("thr_sigsetmask: %s", strerror(err));
66933707f3Ssthen #  else
67933707f3Ssthen 	/* have nothing, do single process signal mask */
68933707f3Ssthen 	if(sigprocmask(SIG_SETMASK, &sigset, NULL))
69933707f3Ssthen 		fatal_exit("sigprocmask: %s", strerror(errno));
70933707f3Ssthen #  endif /* HAVE_SOLARIS_THREADS */
71933707f3Ssthen #endif /* HAVE_PTHREAD */
72933707f3Ssthen #endif /* have signal stuff */
73933707f3Ssthen }
74933707f3Ssthen 
75933707f3Ssthen /** unblock one signal, so we can catch it */
ub_thread_sig_unblock(int sig)76933707f3Ssthen void ub_thread_sig_unblock(int sig)
77933707f3Ssthen {
78933707f3Ssthen #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
79933707f3Ssthen #  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
80933707f3Ssthen 	int err;
81933707f3Ssthen #  endif
82933707f3Ssthen 	sigset_t sigset;
83933707f3Ssthen 	sigemptyset(&sigset);
84933707f3Ssthen 	sigaddset(&sigset, sig);
85933707f3Ssthen #ifdef HAVE_PTHREAD
86933707f3Ssthen 	if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
87933707f3Ssthen 		fatal_exit("pthread_sigmask: %s", strerror(err));
88933707f3Ssthen #else
89933707f3Ssthen #  ifdef HAVE_SOLARIS_THREADS
90933707f3Ssthen 	if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
91933707f3Ssthen 		fatal_exit("thr_sigsetmask: %s", strerror(err));
92933707f3Ssthen #  else
93933707f3Ssthen 	/* have nothing, do single thread case */
94933707f3Ssthen 	if(sigprocmask(SIG_UNBLOCK, &sigset, NULL))
95933707f3Ssthen 		fatal_exit("sigprocmask: %s", strerror(errno));
96933707f3Ssthen #  endif /* HAVE_SOLARIS_THREADS */
97933707f3Ssthen #endif /* HAVE_PTHREAD */
98933707f3Ssthen #else
99933707f3Ssthen 	(void)sig;
100933707f3Ssthen #endif /* have signal stuff */
101933707f3Ssthen }
102933707f3Ssthen 
103933707f3Ssthen #if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS)
104933707f3Ssthen /**
105933707f3Ssthen  * No threading available: fork a new process.
106933707f3Ssthen  * This means no shared data structure, and no locking.
107933707f3Ssthen  * Only the main thread ever returns. Exits on errors.
108933707f3Ssthen  * @param thr: the location where to store the thread-id.
109933707f3Ssthen  * @param func: function body of the thread. Return value of func is lost.
110933707f3Ssthen  * @param arg: user argument to func.
111933707f3Ssthen  */
112933707f3Ssthen void
ub_thr_fork_create(ub_thread_type * thr,void * (* func)(void *),void * arg)113*77079be7Ssthen ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
114933707f3Ssthen {
115933707f3Ssthen 	pid_t pid = fork();
116933707f3Ssthen 	switch(pid) {
117933707f3Ssthen 	default:	/* main */
118*77079be7Ssthen 			*thr = (ub_thread_type)pid;
119933707f3Ssthen 			return;
120933707f3Ssthen 	case 0: 	/* child */
121*77079be7Ssthen 			*thr = (ub_thread_type)getpid();
122933707f3Ssthen 			(void)(*func)(arg);
123933707f3Ssthen 			exit(0);
124933707f3Ssthen 	case -1:	/* error */
125933707f3Ssthen 			fatal_exit("could not fork: %s", strerror(errno));
126933707f3Ssthen 	}
127933707f3Ssthen }
128933707f3Ssthen 
129933707f3Ssthen /**
130933707f3Ssthen  * There is no threading. Wait for a process to terminate.
131*77079be7Ssthen  * Note that ub_thread_type is defined as pid_t.
132933707f3Ssthen  * @param thread: the process id to wait for.
133933707f3Ssthen  */
ub_thr_fork_wait(ub_thread_type thread)134*77079be7Ssthen void ub_thr_fork_wait(ub_thread_type thread)
135933707f3Ssthen {
136933707f3Ssthen 	int status = 0;
137933707f3Ssthen 	if(waitpid((pid_t)thread, &status, 0) == -1)
138933707f3Ssthen 		log_err("waitpid(%d): %s", (int)thread, strerror(errno));
139933707f3Ssthen 	if(status != 0)
140933707f3Ssthen 		log_warn("process %d abnormal exit with status %d",
141933707f3Ssthen 			(int)thread, status);
142933707f3Ssthen }
143933707f3Ssthen #endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */
144933707f3Ssthen 
145933707f3Ssthen #ifdef HAVE_SOLARIS_THREADS
ub_thread_key_get(ub_thread_key_type key)146*77079be7Ssthen void* ub_thread_key_get(ub_thread_key_type key)
147933707f3Ssthen {
148933707f3Ssthen 	void* ret=NULL;
149933707f3Ssthen 	LOCKRET(thr_getspecific(key, &ret));
150933707f3Ssthen 	return ret;
151933707f3Ssthen }
152933707f3Ssthen #endif
153933707f3Ssthen 
154933707f3Ssthen #ifdef HAVE_WINDOWS_THREADS
155933707f3Ssthen /** log a windows GetLastError message */
log_win_err(const char * str,DWORD err)156933707f3Ssthen static void log_win_err(const char* str, DWORD err)
157933707f3Ssthen {
158933707f3Ssthen 	LPTSTR buf;
159933707f3Ssthen 	if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
160933707f3Ssthen 		FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
161933707f3Ssthen 		NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) {
162933707f3Ssthen 		/* could not format error message */
163933707f3Ssthen 		log_err("%s, GetLastError=%d", str, (int)err);
164933707f3Ssthen 		return;
165933707f3Ssthen 	}
166933707f3Ssthen 	log_err("%s, (err=%d): %s", str, (int)err, buf);
167933707f3Ssthen 	LocalFree(buf);
168933707f3Ssthen }
169933707f3Ssthen 
lock_basic_init(lock_basic_type * lock)170*77079be7Ssthen void lock_basic_init(lock_basic_type* lock)
171933707f3Ssthen {
172933707f3Ssthen 	/* implement own lock, because windows HANDLE as Mutex usage
173933707f3Ssthen 	 * uses too many handles and would bog down the whole system. */
174933707f3Ssthen 	(void)InterlockedExchange(lock, 0);
175933707f3Ssthen }
176933707f3Ssthen 
lock_basic_destroy(lock_basic_type * lock)177*77079be7Ssthen void lock_basic_destroy(lock_basic_type* lock)
178933707f3Ssthen {
179933707f3Ssthen 	(void)InterlockedExchange(lock, 0);
180933707f3Ssthen }
181933707f3Ssthen 
lock_basic_lock(lock_basic_type * lock)182*77079be7Ssthen void lock_basic_lock(lock_basic_type* lock)
183933707f3Ssthen {
184933707f3Ssthen 	LONG wait = 1; /* wait 1 msec at first */
185933707f3Ssthen 
186933707f3Ssthen 	while(InterlockedExchange(lock, 1)) {
187933707f3Ssthen 		/* if the old value was 1 then if was already locked */
188933707f3Ssthen 		Sleep(wait); /* wait with sleep */
189933707f3Ssthen 		wait *= 2;   /* exponential backoff for waiting */
190933707f3Ssthen 	}
191933707f3Ssthen 	/* the old value was 0, but we inserted 1, we locked it! */
192933707f3Ssthen }
193933707f3Ssthen 
lock_basic_unlock(lock_basic_type * lock)194*77079be7Ssthen void lock_basic_unlock(lock_basic_type* lock)
195933707f3Ssthen {
196933707f3Ssthen 	/* unlock it by inserting the value of 0. xchg for cache coherency. */
197933707f3Ssthen 	(void)InterlockedExchange(lock, 0);
198933707f3Ssthen }
199933707f3Ssthen 
ub_thread_key_create(ub_thread_key_type * key,void * f)200*77079be7Ssthen void ub_thread_key_create(ub_thread_key_type* key, void* f)
201933707f3Ssthen {
202933707f3Ssthen 	*key = TlsAlloc();
203933707f3Ssthen 	if(*key == TLS_OUT_OF_INDEXES) {
204933707f3Ssthen 		*key = 0;
205933707f3Ssthen 		log_win_err("TlsAlloc Failed(OUT_OF_INDEXES)", GetLastError());
206933707f3Ssthen 	}
207933707f3Ssthen 	else ub_thread_key_set(*key, f);
208933707f3Ssthen }
209933707f3Ssthen 
ub_thread_key_set(ub_thread_key_type key,void * v)210*77079be7Ssthen void ub_thread_key_set(ub_thread_key_type key, void* v)
211933707f3Ssthen {
212933707f3Ssthen 	if(!TlsSetValue(key, v)) {
213933707f3Ssthen 		log_win_err("TlsSetValue failed", GetLastError());
214933707f3Ssthen 	}
215933707f3Ssthen }
216933707f3Ssthen 
ub_thread_key_get(ub_thread_key_type key)217*77079be7Ssthen void* ub_thread_key_get(ub_thread_key_type key)
218933707f3Ssthen {
219933707f3Ssthen 	void* ret = (void*)TlsGetValue(key);
220933707f3Ssthen 	if(ret == NULL && GetLastError() != ERROR_SUCCESS) {
221933707f3Ssthen 		log_win_err("TlsGetValue failed", GetLastError());
222933707f3Ssthen 	}
223933707f3Ssthen 	return ret;
224933707f3Ssthen }
225933707f3Ssthen 
ub_thread_create(ub_thread_type * thr,void * (* func)(void *),void * arg)226*77079be7Ssthen void ub_thread_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
227933707f3Ssthen {
228933707f3Ssthen #ifndef HAVE__BEGINTHREADEX
229933707f3Ssthen 	*thr = CreateThread(NULL, /* default security (no inherit handle) */
230933707f3Ssthen 		0, /* default stack size */
231933707f3Ssthen 		(LPTHREAD_START_ROUTINE)func, arg,
232933707f3Ssthen 		0, /* default flags, run immediately */
233933707f3Ssthen 		NULL); /* do not store thread identifier anywhere */
234933707f3Ssthen #else
2354bfc71b0Ssthen 	/* the beginthreadex routine setups for the C lib; aligns stack */
236*77079be7Ssthen 	*thr=(ub_thread_type)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL);
237933707f3Ssthen #endif
238933707f3Ssthen 	if(*thr == NULL) {
239933707f3Ssthen 		log_win_err("CreateThread failed", GetLastError());
240933707f3Ssthen 		fatal_exit("thread create failed");
241933707f3Ssthen 	}
242933707f3Ssthen }
243933707f3Ssthen 
ub_thread_self(void)244*77079be7Ssthen ub_thread_type ub_thread_self(void)
245933707f3Ssthen {
246933707f3Ssthen 	return GetCurrentThread();
247933707f3Ssthen }
248933707f3Ssthen 
ub_thread_join(ub_thread_type thr)249*77079be7Ssthen void ub_thread_join(ub_thread_type thr)
250933707f3Ssthen {
251933707f3Ssthen 	DWORD ret = WaitForSingleObject(thr, INFINITE);
252933707f3Ssthen 	if(ret == WAIT_FAILED) {
253933707f3Ssthen 		log_win_err("WaitForSingleObject(Thread):WAIT_FAILED",
254933707f3Ssthen 			GetLastError());
255933707f3Ssthen 	} else if(ret == WAIT_TIMEOUT) {
256933707f3Ssthen 		log_win_err("WaitForSingleObject(Thread):WAIT_TIMEOUT",
257933707f3Ssthen 			GetLastError());
258933707f3Ssthen 	}
259933707f3Ssthen 	/* and close the handle to the thread */
260933707f3Ssthen 	if(!CloseHandle(thr)) {
261933707f3Ssthen 		log_win_err("CloseHandle(Thread) failed", GetLastError());
262933707f3Ssthen 	}
263933707f3Ssthen }
264933707f3Ssthen #endif /* HAVE_WINDOWS_THREADS */
265