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
ub_thread_blocksigs(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 */
ub_thread_sig_unblock(int sig)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
ub_thr_fork_create(ub_thread_type * thr,void * (* func)(void *),void * arg)113 ub_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 */
ub_thr_fork_wait(ub_thread_type thread)134 void 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
ub_thread_key_get(ub_thread_key_type key)146 void* 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 */
log_win_err(const char * str,DWORD err)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
lock_basic_init(lock_basic_type * lock)170 void 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
lock_basic_destroy(lock_basic_type * lock)177 void lock_basic_destroy(lock_basic_type* lock)
178 {
179 (void)InterlockedExchange(lock, 0);
180 }
181
lock_basic_lock(lock_basic_type * lock)182 void 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
lock_basic_unlock(lock_basic_type * lock)194 void 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
ub_thread_key_create(ub_thread_key_type * key,void * f)200 void 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
ub_thread_key_set(ub_thread_key_type key,void * v)210 void 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
ub_thread_key_get(ub_thread_key_type key)217 void* 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
ub_thread_create(ub_thread_type * thr,void * (* func)(void *),void * arg)226 void 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
ub_thread_self(void)244 ub_thread_type ub_thread_self(void)
245 {
246 return GetCurrentThread();
247 }
248
ub_thread_join(ub_thread_type thr)249 void 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