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