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