locks.c revision 356345
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. */
50void
51ub_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 */
76void 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 */
112void
113ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
114{
115	pid_t pid = fork();
116	switch(pid) {
117	default:	/* main */
118			*thr = (ub_thread_type)pid;
119			return;
120	case 0: 	/* child */
121			*thr = (ub_thread_type)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_type is defined as pid_t.
132 * @param thread: the process id to wait for.
133 */
134void ub_thr_fork_wait(ub_thread_type 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
146void* ub_thread_key_get(ub_thread_key_type 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 */
156static 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
170void lock_basic_init(lock_basic_type* 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
177void lock_basic_destroy(lock_basic_type* lock)
178{
179	(void)InterlockedExchange(lock, 0);
180}
181
182void lock_basic_lock(lock_basic_type* 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
194void lock_basic_unlock(lock_basic_type* lock)
195{
196	/* unlock it by inserting the value of 0. xchg for cache coherency. */
197	(void)InterlockedExchange(lock, 0);
198}
199
200void ub_thread_key_create(ub_thread_key_type* 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
210void ub_thread_key_set(ub_thread_key_type key, void* v)
211{
212	if(!TlsSetValue(key, v)) {
213		log_win_err("TlsSetValue failed", GetLastError());
214	}
215}
216
217void* ub_thread_key_get(ub_thread_key_type 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
226void ub_thread_create(ub_thread_type* 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 beginthreadex routine setups for the C lib; aligns stack */
236	*thr=(ub_thread_type)_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
244ub_thread_type ub_thread_self(void)
245{
246	return GetCurrentThread();
247}
248
249void ub_thread_join(ub_thread_type 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